diff --git a/client/config/jest/babelTransform.js b/client/config/jest/babelTransform.js new file mode 100644 index 00000000..1529ff91 --- /dev/null +++ b/client/config/jest/babelTransform.js @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Custom babel-jest transformer with an explicit config. +// +// The babel config in package.json ("babel" key) is file-relative, so it is +// never applied to files inside node_modules. ESM-only packages allowed +// through transformIgnorePatterns (e.g. react-leaflet) therefore reached the +// test runtime untransformed. This transformer applies the presets to every +// file Jest transforms, regardless of location. +const babelJest = require('babel-jest') + +module.exports = babelJest.createTransformer({ + presets: [ + [require.resolve('@babel/preset-env'), { targets: { node: 'current' } }], + require.resolve('@babel/preset-react'), + ], + plugins: [ + [ + require.resolve('@babel/plugin-proposal-class-properties'), + { loose: true }, + ], + ], + babelrc: false, + configFile: false, +}) diff --git a/client/config/jest/fileTransform.js b/client/config/jest/fileTransform.js index 01ef824a..391a4190 100644 --- a/client/config/jest/fileTransform.js +++ b/client/config/jest/fileTransform.js @@ -4,7 +4,14 @@ */ const path = require('path') -const camelcase = require('camelcase') + +// camelcase >= 7 is ESM-only and can no longer be require()d here. +const pascalCase = (str) => + str + .split(/[^a-zA-Z0-9]+/) + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join('') // This is a custom Jest transformer turning file imports into filenames. // http://facebook.github.io/jest/docs/en/webpack.html @@ -16,9 +23,7 @@ module.exports = { if (filename.match(/\.svg$/)) { // Based on how SVGR generates a component name: // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 - const pascalCaseFilename = camelcase(path.parse(filename).name, { - pascalCase: true, - }) + const pascalCaseFilename = pascalCase(path.parse(filename).name) const componentName = `Svg${pascalCaseFilename}` return `const React = require('react'); module.exports = { diff --git a/client/config/jest/nodeShims/assert.js b/client/config/jest/nodeShims/assert.js new file mode 100644 index 00000000..085a1e39 --- /dev/null +++ b/client/config/jest/nodeShims/assert.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('assert') diff --git a/client/config/jest/nodeShims/async_hooks.js b/client/config/jest/nodeShims/async_hooks.js new file mode 100644 index 00000000..f27fcb6a --- /dev/null +++ b/client/config/jest/nodeShims/async_hooks.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('async_hooks') diff --git a/client/config/jest/nodeShims/buffer.js b/client/config/jest/nodeShims/buffer.js new file mode 100644 index 00000000..518f53e3 --- /dev/null +++ b/client/config/jest/nodeShims/buffer.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('buffer') diff --git a/client/config/jest/nodeShims/console.js b/client/config/jest/nodeShims/console.js new file mode 100644 index 00000000..66805964 --- /dev/null +++ b/client/config/jest/nodeShims/console.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('console') diff --git a/client/config/jest/nodeShims/crypto.js b/client/config/jest/nodeShims/crypto.js new file mode 100644 index 00000000..cfe16055 --- /dev/null +++ b/client/config/jest/nodeShims/crypto.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('crypto') diff --git a/client/config/jest/nodeShims/diagnostics_channel.js b/client/config/jest/nodeShims/diagnostics_channel.js new file mode 100644 index 00000000..0e317d95 --- /dev/null +++ b/client/config/jest/nodeShims/diagnostics_channel.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('diagnostics_channel') diff --git a/client/config/jest/nodeShims/dns.js b/client/config/jest/nodeShims/dns.js new file mode 100644 index 00000000..91b92d39 --- /dev/null +++ b/client/config/jest/nodeShims/dns.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('dns') diff --git a/client/config/jest/nodeShims/events.js b/client/config/jest/nodeShims/events.js new file mode 100644 index 00000000..7cd57ce9 --- /dev/null +++ b/client/config/jest/nodeShims/events.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('events') diff --git a/client/config/jest/nodeShims/fs.js b/client/config/jest/nodeShims/fs.js new file mode 100644 index 00000000..f5a86a1b --- /dev/null +++ b/client/config/jest/nodeShims/fs.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('fs') diff --git a/client/config/jest/nodeShims/fs/promises.js b/client/config/jest/nodeShims/fs/promises.js new file mode 100644 index 00000000..df437700 --- /dev/null +++ b/client/config/jest/nodeShims/fs/promises.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('fs/promises') diff --git a/client/config/jest/nodeShims/http.js b/client/config/jest/nodeShims/http.js new file mode 100644 index 00000000..5ad7390d --- /dev/null +++ b/client/config/jest/nodeShims/http.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('http') diff --git a/client/config/jest/nodeShims/http2.js b/client/config/jest/nodeShims/http2.js new file mode 100644 index 00000000..737d57f3 --- /dev/null +++ b/client/config/jest/nodeShims/http2.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('http2') diff --git a/client/config/jest/nodeShims/https.js b/client/config/jest/nodeShims/https.js new file mode 100644 index 00000000..f4385332 --- /dev/null +++ b/client/config/jest/nodeShims/https.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('https') diff --git a/client/config/jest/nodeShims/net.js b/client/config/jest/nodeShims/net.js new file mode 100644 index 00000000..f81c980b --- /dev/null +++ b/client/config/jest/nodeShims/net.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('net') diff --git a/client/config/jest/nodeShims/os.js b/client/config/jest/nodeShims/os.js new file mode 100644 index 00000000..cb9051eb --- /dev/null +++ b/client/config/jest/nodeShims/os.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('os') diff --git a/client/config/jest/nodeShims/path.js b/client/config/jest/nodeShims/path.js new file mode 100644 index 00000000..8d718257 --- /dev/null +++ b/client/config/jest/nodeShims/path.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('path') diff --git a/client/config/jest/nodeShims/perf_hooks.js b/client/config/jest/nodeShims/perf_hooks.js new file mode 100644 index 00000000..41b93be3 --- /dev/null +++ b/client/config/jest/nodeShims/perf_hooks.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('perf_hooks') diff --git a/client/config/jest/nodeShims/process.js b/client/config/jest/nodeShims/process.js new file mode 100644 index 00000000..27d951e0 --- /dev/null +++ b/client/config/jest/nodeShims/process.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('process') diff --git a/client/config/jest/nodeShims/querystring.js b/client/config/jest/nodeShims/querystring.js new file mode 100644 index 00000000..0236105c --- /dev/null +++ b/client/config/jest/nodeShims/querystring.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('querystring') diff --git a/client/config/jest/nodeShims/stream.js b/client/config/jest/nodeShims/stream.js new file mode 100644 index 00000000..4304b9f3 --- /dev/null +++ b/client/config/jest/nodeShims/stream.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('stream') diff --git a/client/config/jest/nodeShims/stream/web.js b/client/config/jest/nodeShims/stream/web.js new file mode 100644 index 00000000..56e0f218 --- /dev/null +++ b/client/config/jest/nodeShims/stream/web.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('stream/web') diff --git a/client/config/jest/nodeShims/string_decoder.js b/client/config/jest/nodeShims/string_decoder.js new file mode 100644 index 00000000..6104d33f --- /dev/null +++ b/client/config/jest/nodeShims/string_decoder.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('string_decoder') diff --git a/client/config/jest/nodeShims/timers.js b/client/config/jest/nodeShims/timers.js new file mode 100644 index 00000000..bcb29577 --- /dev/null +++ b/client/config/jest/nodeShims/timers.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('timers') diff --git a/client/config/jest/nodeShims/timers/promises.js b/client/config/jest/nodeShims/timers/promises.js new file mode 100644 index 00000000..a665ffd2 --- /dev/null +++ b/client/config/jest/nodeShims/timers/promises.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('timers/promises') diff --git a/client/config/jest/nodeShims/tls.js b/client/config/jest/nodeShims/tls.js new file mode 100644 index 00000000..2824ec36 --- /dev/null +++ b/client/config/jest/nodeShims/tls.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('tls') diff --git a/client/config/jest/nodeShims/tty.js b/client/config/jest/nodeShims/tty.js new file mode 100644 index 00000000..30b5ef73 --- /dev/null +++ b/client/config/jest/nodeShims/tty.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('tty') diff --git a/client/config/jest/nodeShims/url.js b/client/config/jest/nodeShims/url.js new file mode 100644 index 00000000..9498116c --- /dev/null +++ b/client/config/jest/nodeShims/url.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('url') diff --git a/client/config/jest/nodeShims/util.js b/client/config/jest/nodeShims/util.js new file mode 100644 index 00000000..b8405103 --- /dev/null +++ b/client/config/jest/nodeShims/util.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('util') diff --git a/client/config/jest/nodeShims/util/types.js b/client/config/jest/nodeShims/util/types.js new file mode 100644 index 00000000..13b6deb9 --- /dev/null +++ b/client/config/jest/nodeShims/util/types.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('util/types') diff --git a/client/config/jest/nodeShims/worker_threads.js b/client/config/jest/nodeShims/worker_threads.js new file mode 100644 index 00000000..871c4421 --- /dev/null +++ b/client/config/jest/nodeShims/worker_threads.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('worker_threads') diff --git a/client/config/jest/nodeShims/zlib.js b/client/config/jest/nodeShims/zlib.js new file mode 100644 index 00000000..2e936bd0 --- /dev/null +++ b/client/config/jest/nodeShims/zlib.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Jest 26 can't resolve `node:`-prefixed core modules (used by newer +// dependencies such as cheerio); moduleNameMapper points them here. +module.exports = require('zlib') diff --git a/client/config/jest/testPolyfills.js b/client/config/jest/testPolyfills.js new file mode 100644 index 00000000..8b11872c --- /dev/null +++ b/client/config/jest/testPolyfills.js @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Test-only polyfills. jsdom 16 doesn't expose TextEncoder/TextDecoder, +// web streams, Blob or MessageChannel, which newer dependencies +// (e.g. undici via cheerio/enzyme) require. This file is referenced only +// from Jest's setupFiles, never from webpack, so requiring node core +// modules here is safe. + +const util = require('util') +if (typeof global.TextEncoder === 'undefined') { + global.TextEncoder = util.TextEncoder +} +if (typeof global.TextDecoder === 'undefined') { + global.TextDecoder = util.TextDecoder +} + +const streamWeb = require('stream/web') +for (const name of [ + 'ReadableStream', + 'WritableStream', + 'TransformStream', + 'ByteLengthQueuingStrategy', + 'CountQueuingStrategy', +]) { + if (typeof global[name] === 'undefined' && streamWeb[name]) { + global[name] = streamWeb[name] + } +} + +const buffer = require('buffer') +if (typeof global.Blob === 'undefined' && buffer.Blob) { + global.Blob = buffer.Blob +} + +const workerThreads = require('worker_threads') +for (const name of ['MessageChannel', 'MessagePort']) { + if (typeof global[name] === 'undefined' && workerThreads[name]) { + global[name] = workerThreads[name] + } +} diff --git a/client/package.json b/client/package.json index 87f3d26e..f753793c 100644 --- a/client/package.json +++ b/client/package.json @@ -21,6 +21,7 @@ "prettier": "prettier --write \"src/**/*.{js,jsx,mjs,json,scss}\"", "lint": "trunk fmt", "test": "node scripts/test.js", + "test:e2e": "node scripts/test.js --testPathIgnorePatterns=never-ignore-anything --testPathPattern=src/e2etests", "test:watch": "node scripts/test.js --watch", "precommit": "lint-staged", "deepClean": "rm -rf yarn-error.log node_modules yarn.lock package-lock.json", @@ -65,22 +66,30 @@ "browserslist": [">2%", "last 3 versions", "Firefox ESR", "not ie < 9"], "jest": { "collectCoverageFrom": ["src/**/*.{js,jsx,mjs}"], - "setupFiles": ["/config/polyfills.js"], + "setupFiles": [ + "/config/polyfills.js", + "/config/jest/testPolyfills.js" + ], "testEnvironment": "jsdom", "testMatch": [ "/src/**/__tests__/**/*.{js,jsx,mjs}", "/src/**/?(*.)(spec|test).{js,jsx,mjs}" ], "testResultsProcessor": "jest-teamcity", + "testPathIgnorePatterns": ["/src/e2etests/"], "transform": { - "^.+\\.(js|jsx|mjs)$": "/node_modules/babel-jest", + "^.+\\.(js|jsx|mjs)$": "/config/jest/babelTransform.js", "^.+\\.css$": "/config/jest/cssTransform.js", "^(?!.*\\.(js|jsx|mjs|css|json)$)": "/config/jest/fileTransform.js" }, - "transformIgnorePatterns": ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"], + "transformIgnorePatterns": [ + "[/\\\\]node_modules[/\\\\](?!(react-leaflet|@react-leaflet)[/\\\\]).+\\.(js|jsx|mjs)$" + ], "modulePaths": ["./src/"], "moduleNameMapper": { - "^react-native$": "react-native-web" + "^react-native$": "react-native-web", + "^node:(.*)$": "/config/jest/nodeShims/$1.js", + "^cheerio/lib/utils$": "/node_modules/cheerio/dist/commonjs/utils.js" }, "moduleFileExtensions": [ "web.js",