From 6bedfaaad92fba278a7b3778b345a30e4394076e Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Thu, 15 Aug 2024 16:05:11 +0200 Subject: [PATCH 01/17] feat: add @ima/testing-library --- package-lock.json | 204 ++++++++++-------- package.json | 1 + packages/core/src/boot.ts | 2 +- packages/react-page-renderer/package.json | 2 +- packages/server/types.d.ts | 2 + packages/testing-library/.npmignore | 4 + packages/testing-library/LICENSE | 21 ++ packages/testing-library/README.md | 106 +++++++++ packages/testing-library/jest.config.js | 6 + packages/testing-library/package.json | 48 +++++ packages/testing-library/setupJest.js | 0 .../testing-library/src/app/config/bind.ts | 53 +++++ .../src/app/config/settings.ts | 45 ++++ packages/testing-library/src/app/main.ts | 15 ++ packages/testing-library/src/configuration.ts | 57 +++++ packages/testing-library/src/helpers.ts | 11 + packages/testing-library/src/index.ts | 2 + packages/testing-library/src/jest-preset.ts | 76 +++++++ .../src/jestSetupFileAfterEnv.ts | 6 + packages/testing-library/src/localization.ts | 80 +++++++ packages/testing-library/src/rtl.tsx | 96 +++++++++ packages/testing-library/tsconfig.json | 17 ++ 22 files changed, 759 insertions(+), 95 deletions(-) create mode 100644 packages/testing-library/.npmignore create mode 100644 packages/testing-library/LICENSE create mode 100644 packages/testing-library/README.md create mode 100644 packages/testing-library/jest.config.js create mode 100644 packages/testing-library/package.json create mode 100644 packages/testing-library/setupJest.js create mode 100644 packages/testing-library/src/app/config/bind.ts create mode 100644 packages/testing-library/src/app/config/settings.ts create mode 100644 packages/testing-library/src/app/main.ts create mode 100644 packages/testing-library/src/configuration.ts create mode 100644 packages/testing-library/src/helpers.ts create mode 100644 packages/testing-library/src/index.ts create mode 100644 packages/testing-library/src/jest-preset.ts create mode 100644 packages/testing-library/src/jestSetupFileAfterEnv.ts create mode 100644 packages/testing-library/src/localization.ts create mode 100644 packages/testing-library/src/rtl.tsx create mode 100644 packages/testing-library/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 755e5950f2..74fb4d0211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "./packages/devtools", "./packages/error-overlay", "./packages/react-page-renderer", + "./packages/testing-library", "./packages/storybook-integration", "./website" ], @@ -100,6 +101,13 @@ "node": ">=0.10.0" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "license": "MIT", + "peer": true + }, "node_modules/@algolia/autocomplete-core": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", @@ -4938,6 +4946,10 @@ "resolved": "packages/storybook-integration", "link": true }, + "node_modules/@ima/testing-library": { + "resolved": "packages/testing-library", + "link": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -7758,29 +7770,30 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", - "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", - "dev": true, + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/dom/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -7792,7 +7805,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7802,22 +7815,74 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/@testing-library/jest-dom": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", + "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "license": "MIT", + "peer": true + }, "node_modules/@testing-library/react": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.0.0.tgz", - "integrity": "sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==", - "dev": true, + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.0.tgz", + "integrity": "sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", - "@types/react-dom": "^18.0.0" + "@babel/runtime": "^7.12.5" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, "node_modules/@tootallnate/once": { @@ -7841,7 +7906,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "dev": true + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.1", @@ -8293,7 +8358,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -9780,12 +9845,13 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/arr-diff": { @@ -12914,6 +12980,13 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "license": "MIT", + "peer": true + }, "node_modules/cssdb": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.2.tgz", @@ -13845,35 +13918,6 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", - "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.1", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -14019,7 +14063,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -14183,7 +14226,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true + "peer": true }, "node_modules/dom-converter": { "version": "0.2.0", @@ -14674,26 +14717,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.14.tgz", @@ -21634,7 +21657,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -22087,7 +22110,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, "engines": { "node": ">=4" } @@ -26154,8 +26176,7 @@ "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-json-tree": { "version": "0.18.0", @@ -26545,7 +26566,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -29170,18 +29190,6 @@ "graceful-fs": "^4.1.3" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -29424,7 +29432,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, "dependencies": { "min-indent": "^1.0.0" }, @@ -34023,7 +34030,7 @@ }, "devDependencies": { "@cfaester/enzyme-adapter-react-18": "^0.7.0", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^16.0.0", "@types/react": "^18.0.33", "@types/react-dom": "^18.0.6", "@types/webpack-env": "^1.16.3", @@ -34081,6 +34088,17 @@ "webpack": ">=5.x" } }, + "packages/testing-library": { + "version": "19.6.0", + "license": "MIT", + "peerDependencies": { + "@ima/core": ">=19.0.0", + "@ima/react-page-renderer": ">=19.0.0", + "@testing-library/dom": ">=10.0.0", + "@testing-library/jest-dom": ">=6.0.0", + "@testing-library/react": ">=16.0.0" + } + }, "website": { "name": "@ima/docs", "version": "0.0.0", diff --git a/package.json b/package.json index ce9249abbf..e1a3fce2ba 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "./packages/devtools", "./packages/error-overlay", "./packages/react-page-renderer", + "./packages/testing-library", "./packages/storybook-integration", "./website" ], diff --git a/packages/core/src/boot.ts b/packages/core/src/boot.ts index eefb797d73..070b456f40 100644 --- a/packages/core/src/boot.ts +++ b/packages/core/src/boot.ts @@ -55,7 +55,7 @@ export interface Resources { */ export interface Environment { [key: string]: unknown; - $Debug: GlobalImaObject['$Version']; + $Debug: GlobalImaObject['$Debug']; $Language: Record; $Version: GlobalImaObject['$Version']; $App: GlobalImaObject['$App']; diff --git a/packages/react-page-renderer/package.json b/packages/react-page-renderer/package.json index 33b2d19536..d7150ba79b 100644 --- a/packages/react-page-renderer/package.json +++ b/packages/react-page-renderer/package.json @@ -65,7 +65,7 @@ }, "devDependencies": { "@cfaester/enzyme-adapter-react-18": "^0.7.0", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^16.0.0", "@types/react": "^18.0.33", "@types/react-dom": "^18.0.6", "@types/webpack-env": "^1.16.3", diff --git a/packages/server/types.d.ts b/packages/server/types.d.ts index 8919b4409a..8a92aba009 100644 --- a/packages/server/types.d.ts +++ b/packages/server/types.d.ts @@ -23,6 +23,8 @@ declare module '@ima/server' { } export function createIMAServer(params: { + applicationFolder?: string; + processEnvironment?: (environment: Environment) => Environment; environment?: Environment; logger?: any; emitter?: Emitter; diff --git a/packages/testing-library/.npmignore b/packages/testing-library/.npmignore new file mode 100644 index 0000000000..00d1f7e29d --- /dev/null +++ b/packages/testing-library/.npmignore @@ -0,0 +1,4 @@ +* +!dist/**/* +!package.json +!jest-preset.js diff --git a/packages/testing-library/LICENSE b/packages/testing-library/LICENSE new file mode 100644 index 0000000000..a613dddbf1 --- /dev/null +++ b/packages/testing-library/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Seznam.cz a.s. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/testing-library/README.md b/packages/testing-library/README.md new file mode 100644 index 0000000000..6d54ce7bdd --- /dev/null +++ b/packages/testing-library/README.md @@ -0,0 +1,106 @@ +

+ +

+ +

@ima/testing-library

+

Testing library for IMA.js applications. +

+ +--- + +## IMA Testing Library + +The `@ima/testing-library` contains utilities for testing IMA.js applications. It provides integration with [Jest](https://jestjs.io), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) (RTL for short) amd [Testing Library Jest DOM](https://testing-library.com/docs/ecosystem-jest-dom). + +## Installation + +Install the new dependencies. Note that RTL dependencies are only peer dependencies and you should specify them in your project. + +```bash +npm install -D @ima/testing-library @testing-library/dom @testing-library/jest-dom @testing-library/react +``` + +Configure jest preset in your jest config file. + +```json +{ + "preset": "@ima/testing-library" +} +``` + +Everything should start working out of the box for a typical IMA.js application. If you are trying to setup this library in a monorepo or an npm package, you might have to do some tweaks with the configuration. In this case, you need the jest config file to be in non-json format. + +```javascript +const { setImaTestingLibraryConfig, FALLBACK_APP_MAIN_PATH } = require('@ima/testing-library'); + +setImaTestingLibraryConfig({ + // your custom config + appMainPath: FALLBACK_APP_MAIN_PATH, // There is a default app main file as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real app main file. + imaConfigPath: 'path/to/your/ima.config.js', + applicationFolder: '/path/to/folder/containing/server/folder', +}); + +module.exports = { + preset: '@ima/testing-library' +}; +``` + +## Usage + +IMA Testing Library is re-exporting everything from `@testing-library/react`. It provides the default context wrapper for the `render` method. Thanks to this, the default example from the React Testing Library documentation will work out of the box. You just need to import the `render` method from the `@ima/testing-library` package. + +```javascript +import { render } from '@ima/testing-library'; + +test('renders learn react link', () => { + const { getByText } = render(); + const linkElement = getByText(/learn react/i); + + expect(linkElement).toBeInTheDocument(); +}); +``` + +You might need to specify custom additions to the context, or mock some parts of the IMA application. You can do this by providing a custom context wrapper and using the `@ima/testing-library` specific utilities. + +```javascript +import { render, getContextWrapper, getContextValue, initImaApp } from '@ima/testing-library'; + +test('renders learn react link with custom context wrapper', () => { + const ContextWrapper = getContextWrapper(); + const { getByText } = render(, { + wrapper: , + }); + const linkElement = getByText(/learn react/i); + + expect(linkElement).toBeInTheDocument(); +}); + +test('renders learn react link with custom context value', () => { + const contextValue = getContextValue(); + + contextValue.$Utils.$Foo = jest.fn(() => 'bar'); + + const ContextWrapper = getContextWrapper(contextValue); + const { getByText } = render(, { + wrapper: ContextWrapper, + }); + const linkElement = getByText(/learn react/i); + + expect(linkElement).toBeInTheDocument(); +}); + +test('renders learn react link with custom app configuration', () => { + const app = initImaApp(); + + app.oc.get('$Utils').$Foo = jest.fn(() => 'bar'); + + const contextValue = getContextValue(app); + const ContextWrapper = getContextWrapper(contextValue); + const { getByText } = render(, { + wrapper: ContextWrapper, + }); + const linkElement = getByText(/learn react/i); + + expect(linkElement).toBeInTheDocument(); +}); +``` diff --git a/packages/testing-library/jest.config.js b/packages/testing-library/jest.config.js new file mode 100644 index 0000000000..9f891301ff --- /dev/null +++ b/packages/testing-library/jest.config.js @@ -0,0 +1,6 @@ +const defaultConfig = require('../../jest.config.base.js'); + +module.exports = { + ...defaultConfig, + testRegex: '(/__tests__/).*Spec\\.[jt]s$', +}; diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json new file mode 100644 index 0000000000..b9933fc41b --- /dev/null +++ b/packages/testing-library/package.json @@ -0,0 +1,48 @@ +{ + "name": "@ima/testing-library", + "version": "19.6.0", + "description": "Testing library for IMA.js applications.", + "keywords": [ + "IMA.js", + "react", + "testing", + "library" + ], + "bugs": { + "url": "https://github.com/seznam/ima/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/seznam/ima.git", + "directory": "packages/testing-library" + }, + "license": "MIT", + "author": "Filip Satek ", + "scripts": { + "dev": "node ../plugin-cli/dist/bin/ima-plugin.js dev", + "build": "node ../plugin-cli/dist/bin/ima-plugin.js build", + "link": "node ../plugin-cli/dist/bin/ima-plugin.js link", + "lint": "eslint './**/*.{js,jsx,ts,tsx}'", + "test": "jest -c jest.config.js" + }, + "exports": { + ".": { + "types": "./dist/esm/index.d.ts", + "import": "./dist/esm/index.js", + "default": "./dist/cjs/index.js" + }, + "./jest-preset": "./dist/cjs/jest-preset.js", + "./jestSetupFileAfterEnv": "./dist/cjs/jestSetupFileAfterEnv.js" + }, + "peerDependencies": { + "@ima/core": ">=19.0.0", + "@ima/react-page-renderer": ">=19.0.0", + "@testing-library/dom": ">=10.0.0", + "@testing-library/jest-dom": ">=6.0.0", + "@testing-library/react": ">=16.0.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/packages/testing-library/setupJest.js b/packages/testing-library/setupJest.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/testing-library/src/app/config/bind.ts b/packages/testing-library/src/app/config/bind.ts new file mode 100644 index 0000000000..03f2166b66 --- /dev/null +++ b/packages/testing-library/src/app/config/bind.ts @@ -0,0 +1,53 @@ +import { + ComponentUtils, + InitBindFunction, + PageRenderer, + Window, +} from '@ima/core'; +import { + defaultCssClasses as cssClassNameProcessor, + PageRendererFactory, + ServerPageRenderer, +} from '@ima/react-page-renderer'; +import { ClientPageRenderer } from '@ima/react-page-renderer/renderer/ClientPageRenderer'; + +declare module '@ima/core' { + interface OCAliasMap { + $CssClasses: () => typeof cssClassNameProcessor; + $PageRendererFactory: PageRendererFactory; + } +} + +export const initBindApp: InitBindFunction = (ns, oc) => { + // UI components + oc.bind('$CssClasses', function () { + return cssClassNameProcessor; + }); + + // You can set own Component utils here + oc.get(ComponentUtils).register({ + $CssClasses: '$CssClasses', + }); + + oc.inject(PageRendererFactory, [ComponentUtils]); + oc.bind('$PageRendererFactory', PageRendererFactory); + + if (oc.get(Window).isClient()) { + oc.provide(PageRenderer, ClientPageRenderer, [ + PageRendererFactory, + '$Helper', + '$Dispatcher', + '$Settings', + Window, + ]); + } else { + oc.provide(PageRenderer, ServerPageRenderer, [ + PageRendererFactory, + '$Helper', + '$Dispatcher', + '$Settings', + ]); + } + + oc.bind('$PageRenderer', PageRenderer); +}; diff --git a/packages/testing-library/src/app/config/settings.ts b/packages/testing-library/src/app/config/settings.ts new file mode 100644 index 0000000000..03e6b7e572 --- /dev/null +++ b/packages/testing-library/src/app/config/settings.ts @@ -0,0 +1,45 @@ +import { InitSettingsFunction } from '@ima/core'; + +export const initSettings: InitSettingsFunction = (ns, oc, config) => { + return { + prod: { + $Version: config.$Version, + $Http: { + defaultRequestOptions: { + timeout: 7000, // Request timeout + repeatRequest: 0, // Count of automatic repeated request after failing request. + ttl: 60000, // Default time to live for cached request in ms. + fetchOptions: { + mode: 'cors', + headers: { + // Set default request headers + Accept: 'application/json', + 'Accept-Language': config.$Language, + }, + }, + cache: false, // if value exists in cache then returned it else make request to remote server. + }, + cacheOptions: { + prefix: 'http.', // Cache key prefix for response bodies (already parsed as JSON) of completed HTTP requests. + }, + }, + $Router: { + /** + * Middleware execution timeout, see https://imajs.io/basic-features/routing/middlewares#execution-timeout + * for more information. + */ + middlewareTimeout: 30000, + }, + $Cache: { + enabled: false, //Turn on/off cache for all application. + ttl: 60000, // Default time to live for cached value in ms. + }, + $Page: { + $Render: { + documentView: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + masterElementId: 'page', + }, + }, + }, + }; +}; diff --git a/packages/testing-library/src/app/main.ts b/packages/testing-library/src/app/main.ts new file mode 100644 index 0000000000..f3387a4c9a --- /dev/null +++ b/packages/testing-library/src/app/main.ts @@ -0,0 +1,15 @@ +import * as ima from '@ima/core'; + +import { initBindApp } from './config/bind'; +import { initSettings } from './config/settings'; + +const getInitialAppConfigFunctions = () => { + return { + initBindApp, + initRoutes: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + initServicesApp: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + initSettings, + }; +}; + +export { getInitialAppConfigFunctions, ima }; diff --git a/packages/testing-library/src/configuration.ts b/packages/testing-library/src/configuration.ts new file mode 100644 index 0000000000..8df5a9c584 --- /dev/null +++ b/packages/testing-library/src/configuration.ts @@ -0,0 +1,57 @@ +import type { Environment } from '@ima/core'; + +export interface Configuration { + /** + * The path to the main application file. This file should be exporting getInitialAppConfigFunctions and ima keys. + */ + appMainPath: string; + /** + * The path to the IMA configuration file. This can be only configured once before first `initImaApp` call and cannot be reconfigured later. + */ + imaConfigPath: string; + /** + * The protocol of the application. This can be only configured in the jest config file and cannot be reconfigured later. + */ + protocol: string; + /** + * The host of the application. This can be only configured in the jest config file and cannot be reconfigured later. + */ + host: string; + /** + * The locale of the application. This will affect the language of the application. + */ + locale: string; + /** + * The process environment configuration. This allows you to change the environment configuration that will be available in jsdom. + * This can be only configured in the jest config file and cannot be reconfigured later. + */ + processEnvironment: (env: Environment) => Environment; + /** + * The path to the application folder. This can be only configured in the jest config file and cannot be reconfigured later. + */ + applicationFolder: string | undefined; +} + +const configuration: Configuration = { + appMainPath: 'app/main.js', + imaConfigPath: 'ima.config.js', + protocol: 'https:', + host: 'imajs.io', + locale: 'en', + processEnvironment: env => env, + applicationFolder: undefined, +}; + +/** + * Get the current configuration. + */ +export function getImaTestingLibraryConfig() { + return configuration; +} + +/** + * Modify the current configuration. + */ +export function setImaTestingLibraryConfig(config: Partial) { + Object.assign(configuration, config); +} diff --git a/packages/testing-library/src/helpers.ts b/packages/testing-library/src/helpers.ts new file mode 100644 index 0000000000..40f856acec --- /dev/null +++ b/packages/testing-library/src/helpers.ts @@ -0,0 +1,11 @@ +import path from 'path'; + +/** + * Requires specified file from projectPath + * + * @param {string} projectPath relative project path to a file + * @returns {*} File exports + */ +export function requireFromProject(projectPath: string) { + return require(path.resolve(projectPath)); +} diff --git a/packages/testing-library/src/index.ts b/packages/testing-library/src/index.ts new file mode 100644 index 0000000000..3c364ab191 --- /dev/null +++ b/packages/testing-library/src/index.ts @@ -0,0 +1,2 @@ +export * from './rtl'; +export * from './configuration'; diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts new file mode 100644 index 0000000000..98732d6f73 --- /dev/null +++ b/packages/testing-library/src/jest-preset.ts @@ -0,0 +1,76 @@ +import { createIMAServer } from '@ima/server'; +import type { Config } from 'jest'; + +import { getImaTestingLibraryConfig } from './configuration'; + +const imaTestingLibraryConfig = getImaTestingLibraryConfig(); + +/** + * Get response content from @ima/server. + */ +async function _getIMAResponseContent(): Promise { + // Mock devUtils to override manifest loading + const devUtils = { + manifestRequire: () => ({}), + }; + + // Prepare serverApp with environment override + const { serverApp } = await createIMAServer({ + devUtils, + applicationFolder: imaTestingLibraryConfig.applicationFolder, + processEnvironment: currentEnvironment => + imaTestingLibraryConfig.processEnvironment({ + ...currentEnvironment, + $Server: { + ...currentEnvironment.$Server, + concurrency: 0, + serveSPA: { + allow: true, + }, + }, + $Debug: true, + }), + }); + + // Generate request response + const response = await serverApp.requestHandler( + { + get: () => '', + headers: () => '', + originalUrl: imaTestingLibraryConfig.host, + protocol: imaTestingLibraryConfig.protocol.replace(':', ''), + }, + { + status: () => 200, + send: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + set: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + locals: { + language: imaTestingLibraryConfig.locale, + host: imaTestingLibraryConfig.host, + protocol: imaTestingLibraryConfig.protocol, + path: '', + root: '', + languagePartPath: '', + }, + } + ); + + return response.content; +} + +/** + * Jest configuration for IMA testing library. + * We are entering undocumented territory here, jestConfig is a promise, but documentation does not mention, if it is allowed. + * It would be nice if there was a synchronous and more straightforward way of generating IMA SPA content. + */ +const jestConfig: Promise = (async () => ({ + setupFiles: ['@ima/core/setupJest.js'], + setupFilesAfterEnv: ['@ima/testing-library/jestSetupFileAfterEnv'], + testEnvironment: 'jsdom', + testEnvironmentOptions: { + html: await _getIMAResponseContent(), + url: `${imaTestingLibraryConfig.protocol}//${imaTestingLibraryConfig.host}/`, + }, +}))(); + +export default jestConfig; diff --git a/packages/testing-library/src/jestSetupFileAfterEnv.ts b/packages/testing-library/src/jestSetupFileAfterEnv.ts new file mode 100644 index 0000000000..ee0f0693eb --- /dev/null +++ b/packages/testing-library/src/jestSetupFileAfterEnv.ts @@ -0,0 +1,6 @@ +import '@testing-library/jest-dom'; +// @TODO It would be nice to mock fetch also, but there are some issues with fetch-mock being esm only, while +// we are importing it from commonjs. We should investigate this further. +// import fetchMock from 'fetch-mock'; + +// global.fetch = fetchMock.sandbox() as typeof fetch; diff --git a/packages/testing-library/src/localization.ts b/packages/testing-library/src/localization.ts new file mode 100644 index 0000000000..a4a07b6c17 --- /dev/null +++ b/packages/testing-library/src/localization.ts @@ -0,0 +1,80 @@ +import path from 'path'; + +import { assignRecursively } from '@ima/helpers'; +import MessageFormat from '@messageformat/core'; +import globby from 'globby'; + +import { requireFromProject } from './helpers'; + +/** + * @TODO The localization logic should be mostly taken from https://github.com/seznam/ima/blob/master/packages/cli/src/webpack/languages.ts + * This solution is similar, but not the same and there can be some inconsistencies. + */ + +/** + * Generates IMA formatted dictionary + * + * @param {string} locale + * @returns {object} + */ +function generateDictionary(locale = 'cs') { + // @TODO: locale should be taken from config and should be en by default + const { languages } = requireFromProject('./ima.config.js'); + const mf = new MessageFormat(locale); + const dictionaries: Record = {}; + const langFileGlobs = languages[locale]; + + globby.sync(langFileGlobs).forEach(file => { + try { + const filename = path + .basename(file) + .replace(locale.toUpperCase() + path.extname(file), ''); + + const dictJson = requireFromProject(file); + + dictionaries[filename] = assignRecursively( + dictionaries[filename] ?? {}, + _deepMapValues(dictJson, mf.compile.bind(mf)) + ); + } catch (e) { + console.error( + `Tried to load dictionary JSON at path "${file}", but recieved following error.` + ); + console.error(e); + } + }); + + return dictionaries; +} + +/** + * Apply function through full object or array values + * + * @param {object | Array} obj object to be manipulated + * @param {Function} fn function to run on values + * @returns {object | Array} + */ +function _deepMapValues( + obj: object | Array, + fn: typeof MessageFormat.prototype.compile +): + | ReturnType + | Record + | string { + if (Array.isArray(obj)) { + return obj.map(val => _deepMapValues(val, fn)); + } else if (typeof obj === 'function') { + // Skip already mapped values + return obj; + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce((acc, current) => { + // @ts-expect-error I don't know how to type `obj[current]`, help me! + acc[current] = _deepMapValues(obj[current], fn); + return acc; + }, {} as Record); + } else { + return fn(obj); + } +} + +export { generateDictionary }; diff --git a/packages/testing-library/src/rtl.tsx b/packages/testing-library/src/rtl.tsx new file mode 100644 index 0000000000..783abb02eb --- /dev/null +++ b/packages/testing-library/src/rtl.tsx @@ -0,0 +1,96 @@ +import path from 'node:path'; + +import * as imaCore from '@ima/core'; +import { PageContext } from '@ima/react-page-renderer'; +import { render, RenderOptions } from '@testing-library/react'; // eslint-disable-line import/named +import React, { ReactElement } from 'react'; + +import { getImaTestingLibraryConfig } from './configuration'; +import { requireFromProject } from './helpers'; +import { generateDictionary } from './localization'; + +export interface ContextValue { + $Utils: imaCore.Utils; +} + +export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, 'app/main.js'); + +// Some operations take way too long to be executed with each render call, +// so we need to cache these values +const mainFile: Record< + string, + { + ima: typeof imaCore; + getInitialAppConfigFunctions: () => imaCore.InitAppConfig; + } +> = {}; +const dictionary: Record = {}; + +export function initImaApp() { + const config = getImaTestingLibraryConfig(); + + if (!mainFile[config.appMainPath]) { + mainFile[config.appMainPath] = requireFromProject(config.appMainPath); + } + + if (!dictionary[config.locale]) { + dictionary[config.locale] = generateDictionary(config.locale); + } + + const { ima, getInitialAppConfigFunctions } = mainFile[config.appMainPath]; + + // Init language files + // This must be initialized before oc.get('$Dictionary').init() is called (usualy part of initServices) + global.$IMA.i18n = dictionary[config.locale]; + + const app = ima.createImaApp(); + const bootConfig = ima.getClientBootConfig(getInitialAppConfigFunctions()); + + // Init app + ima.bootClientApp(app, bootConfig); + + return app; +} + +export function getContextValue( + app?: ReturnType +): ContextValue { + if (!app) { + app = initImaApp(); + } + + return { $Utils: app.oc.get('$ComponentUtils').getUtils() }; +} + +export function getContextWrapper(contextValue?: ContextValue) { + if (!contextValue) { + contextValue = getContextValue(); + } + + return function IMATestingLibraryContextWrapper({ + children, + }: { + children: React.ReactNode; + }) { + return ( + + {children} + + ); + }; +} + +const customRender = (ui: ReactElement, options?: RenderOptions) => { + let { wrapper, ...rest } = options ?? {}; // eslint-disable-line prefer-const + + if (!wrapper) { + wrapper = getContextWrapper(); + } + + const result = render(ui, { wrapper, ...rest }); + + return result; +}; + +export * from '@testing-library/react'; // eslint-disable-line import/export +export { customRender as render }; // eslint-disable-line import/export diff --git a/packages/testing-library/tsconfig.json b/packages/testing-library/tsconfig.json new file mode 100644 index 0000000000..00b55540fd --- /dev/null +++ b/packages/testing-library/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2018", + "module": "Node16", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable", + ], + "outDir": "dist", + "types": ["webpack", "core", "cli", "react-page-renderer", "less-plugin-glob"] + }, + "include": [ + "src" + ] +} From dfb8dae33b307ad4ebce87ec9eceb748e9e88bc2 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 19 Aug 2024 13:06:04 +0200 Subject: [PATCH 02/17] feat: add more config options to customize app and context; add default server config; rewrite enzyme tests to itl --- jest.config.base.js | 1 - packages/cli/src/webpack/utils.ts | 2 +- packages/core/ima-plugin.config.js | 6 +- packages/core/src/boot.ts | 2 +- packages/create-ima-app/bin/create-ima-app.js | 2 +- .../app/component/card/__tests__/CardSpec.js | 6 +- .../template/common/jest.config.js | 5 +- .../template/common/package.json | 8 +- .../template/common/setupJest.js | 4 - packages/react-page-renderer/jest.config.js | 11 +- packages/react-page-renderer/package.json | 1 - packages/react-page-renderer/setupJest.js | 21 +-- .../src/hooks/__tests__/componentSpec.js | 18 +-- .../src/hooks/__tests__/componentUtilsSpec.js | 4 +- .../src/hooks/__tests__/cssClassesSpec.js | 4 +- .../src/hooks/__tests__/dispatcherSpec.js | 4 +- .../src/hooks/__tests__/eventBusSpec.js | 4 +- .../src/hooks/__tests__/linkSpec.js | 4 +- .../src/hooks/__tests__/localizeSpec.js | 4 +- .../src/hooks/__tests__/pageContextSpec.js | 4 +- .../src/hooks/__tests__/settingsSpec.js | 8 +- .../src/hooks/__tests__/windowEventSpec.js | 6 +- .../react-page-renderer/src/testUtils.jsx | 8 +- packages/testing-library/README.md | 34 ++++- packages/testing-library/src/configuration.ts | 91 ++++++++---- packages/testing-library/src/index.ts | 1 + packages/testing-library/src/jest-preset.ts | 23 +-- .../src/jestSetupFileAfterEnv.ts | 5 - packages/testing-library/src/localization.ts | 9 +- packages/testing-library/src/rtl.tsx | 37 +++-- .../src/server/config/environment.ts | 137 ++++++++++++++++++ .../src/server/template/400.ejs | 14 ++ .../src/server/template/500.ejs | 14 ++ .../src/server/template/spa.ejs | 18 +++ packages/testing-library/src/types.ts | 5 + 35 files changed, 387 insertions(+), 138 deletions(-) delete mode 100644 packages/create-ima-app/template/common/setupJest.js create mode 100644 packages/testing-library/src/server/config/environment.ts create mode 100644 packages/testing-library/src/server/template/400.ejs create mode 100644 packages/testing-library/src/server/template/500.ejs create mode 100644 packages/testing-library/src/server/template/spa.ejs create mode 100644 packages/testing-library/src/types.ts diff --git a/jest.config.base.js b/jest.config.base.js index 5277d3b959..ae581fba2f 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -1,6 +1,5 @@ module.exports = { rootDir: '.', - testEnvironment: 'node', modulePaths: ['/'], setupFiles: ['/setupJest.js'], testRegex: '(/__tests__/).*Spec\\.jsx?$', diff --git a/packages/cli/src/webpack/utils.ts b/packages/cli/src/webpack/utils.ts index 9ff1d6e7d5..dd68a4065f 100644 --- a/packages/cli/src/webpack/utils.ts +++ b/packages/cli/src/webpack/utils.ts @@ -226,7 +226,7 @@ export function requireImaConfig(rootDir = process.cwd()): ImaConfig | null { * @param {ImaCliArgs} args CLI args. * @returns {Promise} Ima config or empty object. */ -export async function resolveImaConfig(args: ImaCliArgs): Promise { +export function resolveImaConfig(args: ImaCliArgs): ImaConfig { const defaultImaConfig: ImaConfig = { publicPath: '/', compress: true, diff --git a/packages/core/ima-plugin.config.js b/packages/core/ima-plugin.config.js index 72e94089fc..6d390203b0 100644 --- a/packages/core/ima-plugin.config.js +++ b/packages/core/ima-plugin.config.js @@ -5,5 +5,9 @@ const { clientServerConfig } = require('@ima/plugin-cli'); */ module.exports = { ...clientServerConfig, - additionalWatchPaths: ['./transform/**/*', './polyfill/**/*'], + additionalWatchPaths: [ + './transform/**/*', + './polyfill/**/*', + './setupJest.js', + ], }; diff --git a/packages/core/src/boot.ts b/packages/core/src/boot.ts index 070b456f40..772edd1d1c 100644 --- a/packages/core/src/boot.ts +++ b/packages/core/src/boot.ts @@ -58,7 +58,7 @@ export interface Environment { $Debug: GlobalImaObject['$Debug']; $Language: Record; $Version: GlobalImaObject['$Version']; - $App: GlobalImaObject['$App']; + $App?: GlobalImaObject['$App']; $Resources?: ( response: unknown, manifest: Manifest, diff --git a/packages/create-ima-app/bin/create-ima-app.js b/packages/create-ima-app/bin/create-ima-app.js index 88e13a8a74..ba2f68c921 100755 --- a/packages/create-ima-app/bin/create-ima-app.js +++ b/packages/create-ima-app/bin/create-ima-app.js @@ -9,7 +9,7 @@ import { create } from '../scripts/create.js'; import { error, warn } from '../scripts/utils.js'; const MIN_NODE_VERSION = 16; -const MAX_NODE_VERSION = 18; +const MAX_NODE_VERSION = 22; const __dirname = path.dirname(fileURLToPath(import.meta.url)); if (process.argv.length === 2) { diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js index a8e4ff3ee9..470845c97f 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js @@ -1,4 +1,4 @@ -import { shallow } from 'enzyme'; +import { render } from '@ima/testing-library'; import { Card } from '../Card'; @@ -10,8 +10,8 @@ describe('Card', () => { children: 'Some content of the card.', }; - const wrapper = shallow(); + const { container } = render(); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); }); diff --git a/packages/create-ima-app/template/common/jest.config.js b/packages/create-ima-app/template/common/jest.config.js index 76bd35f5f2..893f50cc76 100644 --- a/packages/create-ima-app/template/common/jest.config.js +++ b/packages/create-ima-app/template/common/jest.config.js @@ -1,9 +1,8 @@ module.exports = { bail: true, - testEnvironment: 'node', + preset: '@ima/testing-library', modulePaths: ['/'], - setupFiles: ['@ima/core/setupJest.js', '/setupJest.js'], - snapshotSerializers: ['enzyme-to-json/serializer'], + setupFiles: ['@ima/core/setupJest.js'], testRegex: '(/__tests__/).*Spec\\.jsx?$', moduleNameMapper: { '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': diff --git a/packages/create-ima-app/template/common/package.json b/packages/create-ima-app/template/common/package.json index 2784ce51d9..bce5143abf 100644 --- a/packages/create-ima-app/template/common/package.json +++ b/packages/create-ima-app/template/common/package.json @@ -19,10 +19,11 @@ "@babel/preset-react": "^7.16.7", "@ima/cli": "^19.0.1", "@ima/plugin-testing-integration": "4.1.0", + "@ima/testing-library": "../github-ima/ima-testing-library-19.6.0.tgz", "@swc/jest": "^0.2.20", - "@cfaester/enzyme-adapter-react-18": "^0.6.0", - "enzyme": "^3.11.0", - "enzyme-to-json": "^3.6.2", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^16.0.0", "eslint": "^8.11.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-jest": "^26.1.1", @@ -31,6 +32,7 @@ "error-to-json": "2.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.3.1", + "jest-environment-jsdom": "^29.7.0", "prettier": "^2.6.0" }, "dependencies": { diff --git a/packages/create-ima-app/template/common/setupJest.js b/packages/create-ima-app/template/common/setupJest.js deleted file mode 100644 index 14fad19f87..0000000000 --- a/packages/create-ima-app/template/common/setupJest.js +++ /dev/null @@ -1,4 +0,0 @@ -import Adapter from '@cfaester/enzyme-adapter-react-18'; -import enzyme from 'enzyme'; - -enzyme.configure({ adapter: new Adapter() }); diff --git a/packages/react-page-renderer/jest.config.js b/packages/react-page-renderer/jest.config.js index be07cab7fb..18974c00f4 100644 --- a/packages/react-page-renderer/jest.config.js +++ b/packages/react-page-renderer/jest.config.js @@ -1,7 +1,16 @@ +const { + setImaTestingLibraryServerConfig, + FALLBACK_APPLICATION_FOLDER, +} = require('@ima/testing-library'); + const defaultConfig = require('../../jest.config.base.js'); +setImaTestingLibraryServerConfig({ + applicationFolder: FALLBACK_APPLICATION_FOLDER, +}); + module.exports = { ...defaultConfig, - testEnvironment: 'jsdom', + preset: '@ima/testing-library', testRegex: '(/__tests__/).*Spec\\.[jt]s$', }; diff --git a/packages/react-page-renderer/package.json b/packages/react-page-renderer/package.json index d7150ba79b..47989b3bbd 100644 --- a/packages/react-page-renderer/package.json +++ b/packages/react-page-renderer/package.json @@ -64,7 +64,6 @@ "memoize-one": "^6.0.0" }, "devDependencies": { - "@cfaester/enzyme-adapter-react-18": "^0.7.0", "@testing-library/react": "^16.0.0", "@types/react": "^18.0.33", "@types/react-dom": "^18.0.6", diff --git a/packages/react-page-renderer/setupJest.js b/packages/react-page-renderer/setupJest.js index cf260d2f2c..54f6a203a5 100644 --- a/packages/react-page-renderer/setupJest.js +++ b/packages/react-page-renderer/setupJest.js @@ -1,13 +1,8 @@ -const util = require('util'); - -var root = typeof window !== 'undefined' && window !== null ? window : global; - -root.TextEncoder = util.TextEncoder; -root.TextDecoder = util.TextDecoder; - -root.$Debug = true; - -const Adapter = require('@cfaester/enzyme-adapter-react-18').default; -const enzyme = require('enzyme'); - -enzyme.configure({ adapter: new Adapter() }); +const { + setImaTestingLibraryClientConfig, + FALLBACK_APP_MAIN_PATH, +} = require('@ima/testing-library'); + +setImaTestingLibraryClientConfig({ + appMainPath: FALLBACK_APP_MAIN_PATH, +}); diff --git a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js index ee42eabb59..cd8d70e51d 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js @@ -1,13 +1,13 @@ -import { shallow } from 'enzyme'; +import { render } from '@ima/testing-library'; -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useComponent, useOnce } from '../component'; describe('useComponent', () => { let result; it('should return object of component utility functions', () => { - mountHook(() => { + renderHook(() => { result = useComponent(); }, {}); @@ -29,8 +29,6 @@ describe('useComponent', () => { }); describe('useOnce', () => { - let wrapper; - it('should call callback only once', () => { let count = 0; @@ -40,12 +38,12 @@ describe('useOnce', () => { return null; }; - wrapper = shallow(); + const { rerender } = render(); - wrapper.setProps({}); - wrapper.setProps({}); - wrapper.setProps({}); - wrapper.setProps({}); + rerender(); + rerender(); + rerender(); + rerender(); expect(count).toBe(1); }); diff --git a/packages/react-page-renderer/src/hooks/__tests__/componentUtilsSpec.js b/packages/react-page-renderer/src/hooks/__tests__/componentUtilsSpec.js index 0d44f2e28d..cecba4084f 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/componentUtilsSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/componentUtilsSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useComponentUtils } from '../componentUtils'; describe('useComponentUtils', () => { @@ -6,7 +6,7 @@ describe('useComponentUtils', () => { let contextMock = { $Utils: { CustomContextHelper: {} } }; it('should return componentUtils', () => { - mountHook(() => { + renderHook(() => { result = useComponentUtils(); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/cssClassesSpec.js b/packages/react-page-renderer/src/hooks/__tests__/cssClassesSpec.js index 83a1faeba9..6d0f87eead 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/cssClassesSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/cssClassesSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useCssClasses } from '../cssClasses'; describe('useCssClasses', () => { @@ -10,7 +10,7 @@ describe('useCssClasses', () => { }; it('should return shortcut to $CssClasses utility', () => { - mountHook(() => { + renderHook(() => { result = useCssClasses(); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/dispatcherSpec.js b/packages/react-page-renderer/src/hooks/__tests__/dispatcherSpec.js index ab7b695a55..7c4b95febd 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/dispatcherSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/dispatcherSpec.js @@ -1,6 +1,6 @@ import React from 'react'; -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useDispatcher } from '../dispatcher'; describe('useDispatcher', () => { @@ -11,7 +11,7 @@ describe('useDispatcher', () => { }); it('should return `fire` callback', () => { - mountHook(() => { + renderHook(() => { result = useDispatcher(); }); diff --git a/packages/react-page-renderer/src/hooks/__tests__/eventBusSpec.js b/packages/react-page-renderer/src/hooks/__tests__/eventBusSpec.js index dc21793c13..787273f306 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/eventBusSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/eventBusSpec.js @@ -1,6 +1,6 @@ import React from 'react'; -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useEventBus } from '../eventBus'; describe('useEventBus', () => { @@ -19,7 +19,7 @@ describe('useEventBus', () => { }); it('should return `fire` callback', () => { - mountHook(() => { + renderHook(() => { const ref = React.createRef(null); result = useEventBus(ref, 'event', () => {}); diff --git a/packages/react-page-renderer/src/hooks/__tests__/linkSpec.js b/packages/react-page-renderer/src/hooks/__tests__/linkSpec.js index c0e9c9c981..adaf83304c 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/linkSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/linkSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useLink } from '../link'; describe('useLink', () => { @@ -12,7 +12,7 @@ describe('useLink', () => { }; it('should return shortcut to router link', () => { - mountHook(() => { + renderHook(() => { result = useLink(); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/localizeSpec.js b/packages/react-page-renderer/src/hooks/__tests__/localizeSpec.js index 76b72518fc..cca6bf4e4a 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/localizeSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/localizeSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useLocalize } from '../localize'; describe('useLocalize', () => { @@ -12,7 +12,7 @@ describe('useLocalize', () => { }; it('should return shortcut to $Dictionary.get function', () => { - mountHook(() => { + renderHook(() => { result = useLocalize(); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/pageContextSpec.js b/packages/react-page-renderer/src/hooks/__tests__/pageContextSpec.js index f00e88808f..83348a6ab9 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/pageContextSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/pageContextSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { usePageContext } from '../pageContext'; describe('usePageContext', () => { @@ -9,7 +9,7 @@ describe('usePageContext', () => { }; it('should return pageContext', () => { - mountHook(() => { + renderHook(() => { result = usePageContext(); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/settingsSpec.js b/packages/react-page-renderer/src/hooks/__tests__/settingsSpec.js index 7b86774059..25f2f2e961 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/settingsSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/settingsSpec.js @@ -1,4 +1,4 @@ -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useSettings } from '../settings'; describe('useSettings', () => { @@ -15,7 +15,7 @@ describe('useSettings', () => { }; it('should return settings object by default', () => { - mountHook(() => { + renderHook(() => { result = useSettings(); }, contextMock); @@ -23,7 +23,7 @@ describe('useSettings', () => { }); it('should return specific sub-settings for given selector', () => { - mountHook(() => { + renderHook(() => { result = useSettings('$Page.scripts'); }, contextMock); @@ -31,7 +31,7 @@ describe('useSettings', () => { }); it('should return empty object for invalid selectors', () => { - mountHook(() => { + renderHook(() => { result = useSettings('invalid.settings.path'); }, contextMock); diff --git a/packages/react-page-renderer/src/hooks/__tests__/windowEventSpec.js b/packages/react-page-renderer/src/hooks/__tests__/windowEventSpec.js index 1dcd4dd3ce..cd8dd2d0d0 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/windowEventSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/windowEventSpec.js @@ -1,6 +1,6 @@ import React from 'react'; -import { mountHook } from '../../testUtils'; +import { renderHook } from '../../testUtils'; import { useWindowEvent } from '../windowEvent'; describe('useWindowEvent', () => { @@ -29,7 +29,7 @@ describe('useWindowEvent', () => { }); it('should return window and utility functions', () => { - mountHook(() => { + renderHook(() => { result = useWindowEvent('custom-target', 'custom-event', jest.fn()); expect(result).toMatchInlineSnapshot(` @@ -47,7 +47,7 @@ describe('useWindowEvent', () => { it('should bind events correctly', () => { let cb = jest.fn(); - mountHook(() => { + renderHook(() => { result = useWindowEvent('custom-target', 'custom-event', cb, true); expect(contextMock.$Utils.$Window.bindEventListener).toHaveBeenCalledWith( diff --git a/packages/react-page-renderer/src/testUtils.jsx b/packages/react-page-renderer/src/testUtils.jsx index f3a3cb7704..10de47bb09 100644 --- a/packages/react-page-renderer/src/testUtils.jsx +++ b/packages/react-page-renderer/src/testUtils.jsx @@ -1,4 +1,4 @@ -import { mount } from 'enzyme'; +import { render } from '@ima/testing-library'; import { PageContext } from './PageContext'; @@ -9,17 +9,17 @@ import { PageContext } from './PageContext'; * @param {object} props * @returns {ReactWrapper} */ -function mountHook(callback, context = {}, props = {}) { +function renderHook(callback, context = {}, props = {}) { const TestHookComponent = ({ __callback__ }) => { __callback__(); return null; }; - return mount( + return render( ); } -export { mountHook }; +export { renderHook }; diff --git a/packages/testing-library/README.md b/packages/testing-library/README.md index 6d54ce7bdd..272a848d78 100644 --- a/packages/testing-library/README.md +++ b/packages/testing-library/README.md @@ -17,7 +17,7 @@ The `@ima/testing-library` contains utilities for testing IMA.js applications. I Install the new dependencies. Note that RTL dependencies are only peer dependencies and you should specify them in your project. ```bash -npm install -D @ima/testing-library @testing-library/dom @testing-library/jest-dom @testing-library/react +npm install -D @ima/testing-library @testing-library/dom @testing-library/jest-dom @testing-library/react jest-environment-jsdom ``` Configure jest preset in your jest config file. @@ -28,15 +28,23 @@ Configure jest preset in your jest config file. } ``` -Everything should start working out of the box for a typical IMA.js application. If you are trying to setup this library in a monorepo or an npm package, you might have to do some tweaks with the configuration. In this case, you need the jest config file to be in non-json format. +Everything should start working out of the box for a typical IMA.js application. If you are trying to setup this library in a monorepo or an npm package, you might have to do some tweaks with the configuration. + +### Configuration + +There are 2 config functions that you can use to adjust the IMA Testing Library to your specific needs. + +**Server Configuration** + +In this case, you need the jest config file to be in non-json format. + +This configuration should be evaluated in the jest config file. It's config values are used to initialize the JSDOM environment in which the tests are running. ```javascript -const { setImaTestingLibraryConfig, FALLBACK_APP_MAIN_PATH } = require('@ima/testing-library'); +const { setImaTestingLibraryServerConfig } = require('@ima/testing-library'); -setImaTestingLibraryConfig({ +setImaTestingLibraryServerConfig({ // your custom config - appMainPath: FALLBACK_APP_MAIN_PATH, // There is a default app main file as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real app main file. - imaConfigPath: 'path/to/your/ima.config.js', applicationFolder: '/path/to/folder/containing/server/folder', }); @@ -45,6 +53,20 @@ module.exports = { }; ``` +**Client Configuration** + +This configuration should be evaluated in the setup files, or directly in the test files. It's config values are used to initialize the IMA.js application and provide the context for the tests. + +```javascript +const { setImaTestingLibraryClientConfig, FALLBACK_APP_MAIN_PATH } = require('@ima/testing-library'); + +setImaTestingLibraryClientConfig({ + // your custom config + appMainPath: FALLBACK_APP_MAIN_PATH, // There is a default app main file as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real app main file. + imaConfigPath: 'path/to/your/ima.config.js', +}); +``` + ## Usage IMA Testing Library is re-exporting everything from `@testing-library/react`. It provides the default context wrapper for the `render` method. Thanks to this, the default example from the React Testing Library documentation will work out of the box. You just need to import the `render` method from the `@ima/testing-library` package. diff --git a/packages/testing-library/src/configuration.ts b/packages/testing-library/src/configuration.ts index 8df5a9c584..befec4b147 100644 --- a/packages/testing-library/src/configuration.ts +++ b/packages/testing-library/src/configuration.ts @@ -1,57 +1,94 @@ -import type { Environment } from '@ima/core'; +import path from 'node:path'; -export interface Configuration { +import type { createImaApp, Environment } from '@ima/core'; + +import type { ContextValue } from './types'; + +export interface ServerConfiguration { /** - * The path to the main application file. This file should be exporting getInitialAppConfigFunctions and ima keys. + * The protocol of the application. */ - appMainPath: string; + protocol: string; /** - * The path to the IMA configuration file. This can be only configured once before first `initImaApp` call and cannot be reconfigured later. + * The host of the application. */ - imaConfigPath: string; + host: string; /** - * The protocol of the application. This can be only configured in the jest config file and cannot be reconfigured later. + * The process environment configuration. This allows you to change the environment configuration that will be available in jsdom. */ - protocol: string; + processEnvironment: (env: Environment) => Environment; /** - * The host of the application. This can be only configured in the jest config file and cannot be reconfigured later. + * The path to the application folder. */ - host: string; + applicationFolder: string | undefined; +} + +export interface ClientConfiguration { /** - * The locale of the application. This will affect the language of the application. + * The path to the main application file. This file should be exporting getInitialAppConfigFunctions and ima keys. */ - locale: string; + appMainPath: string; /** - * The process environment configuration. This allows you to change the environment configuration that will be available in jsdom. - * This can be only configured in the jest config file and cannot be reconfigured later. + * The path to the IMA configuration file. This can be only configured once before first `initImaApp` call and cannot be reconfigured later. */ - processEnvironment: (env: Environment) => Environment; + rootDir: string; /** - * The path to the application folder. This can be only configured in the jest config file and cannot be reconfigured later. + * The function that will be called after the IMA application is initialized. */ - applicationFolder: string | undefined; + afterInitImaApp: (app: ReturnType) => void; + /** + * The function that will be called after the context value is created. + */ + getContextValue: (app: ReturnType) => ContextValue; } -const configuration: Configuration = { - appMainPath: 'app/main.js', - imaConfigPath: 'ima.config.js', +const serverConfiguration: ServerConfiguration = { protocol: 'https:', host: 'imajs.io', - locale: 'en', processEnvironment: env => env, applicationFolder: undefined, }; +const clientConfiguration: ClientConfiguration = { + appMainPath: 'app/main.js', + rootDir: process.cwd(), + afterInitImaApp: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + getContextValue: app => ({ + $Utils: app.oc.get('$ComponentUtils').getUtils(), + }), +}; + +export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, 'app/main.js'); +export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname); + +/** + * Get the current serverConfiguration. + */ +export function getImaTestingLibraryServerConfig() { + return serverConfiguration; +} + +/** + * Modify the current serverConfiguration. + */ +export function setImaTestingLibraryServerConfig( + config: Partial +) { + Object.assign(serverConfiguration, config); +} + /** - * Get the current configuration. + * Get the current clientConfiguration. */ -export function getImaTestingLibraryConfig() { - return configuration; +export function getImaTestingLibraryClientConfig() { + return clientConfiguration; } /** - * Modify the current configuration. + * Modify the current clientConfiguration. */ -export function setImaTestingLibraryConfig(config: Partial) { - Object.assign(configuration, config); +export function setImaTestingLibraryClientConfig( + config: Partial +) { + Object.assign(clientConfiguration, config); } diff --git a/packages/testing-library/src/index.ts b/packages/testing-library/src/index.ts index 3c364ab191..81fa8a2bd6 100644 --- a/packages/testing-library/src/index.ts +++ b/packages/testing-library/src/index.ts @@ -1,2 +1,3 @@ export * from './rtl'; export * from './configuration'; +export * from './types'; diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts index 98732d6f73..15f3bc7f08 100644 --- a/packages/testing-library/src/jest-preset.ts +++ b/packages/testing-library/src/jest-preset.ts @@ -1,9 +1,9 @@ import { createIMAServer } from '@ima/server'; import type { Config } from 'jest'; -import { getImaTestingLibraryConfig } from './configuration'; +import { getImaTestingLibraryServerConfig } from './configuration'; -const imaTestingLibraryConfig = getImaTestingLibraryConfig(); +const serverConfig = getImaTestingLibraryServerConfig(); /** * Get response content from @ima/server. @@ -17,9 +17,9 @@ async function _getIMAResponseContent(): Promise { // Prepare serverApp with environment override const { serverApp } = await createIMAServer({ devUtils, - applicationFolder: imaTestingLibraryConfig.applicationFolder, + applicationFolder: serverConfig.applicationFolder, processEnvironment: currentEnvironment => - imaTestingLibraryConfig.processEnvironment({ + serverConfig.processEnvironment({ ...currentEnvironment, $Server: { ...currentEnvironment.$Server, @@ -37,21 +37,14 @@ async function _getIMAResponseContent(): Promise { { get: () => '', headers: () => '', - originalUrl: imaTestingLibraryConfig.host, - protocol: imaTestingLibraryConfig.protocol.replace(':', ''), + originalUrl: serverConfig.host, + protocol: serverConfig.protocol.replace(':', ''), }, { status: () => 200, send: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function set: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function - locals: { - language: imaTestingLibraryConfig.locale, - host: imaTestingLibraryConfig.host, - protocol: imaTestingLibraryConfig.protocol, - path: '', - root: '', - languagePartPath: '', - }, + locals: {}, } ); @@ -69,7 +62,7 @@ const jestConfig: Promise = (async () => ({ testEnvironment: 'jsdom', testEnvironmentOptions: { html: await _getIMAResponseContent(), - url: `${imaTestingLibraryConfig.protocol}//${imaTestingLibraryConfig.host}/`, + url: `${serverConfig.protocol}//${serverConfig.host}/`, }, }))(); diff --git a/packages/testing-library/src/jestSetupFileAfterEnv.ts b/packages/testing-library/src/jestSetupFileAfterEnv.ts index ee0f0693eb..7b0828bfa8 100644 --- a/packages/testing-library/src/jestSetupFileAfterEnv.ts +++ b/packages/testing-library/src/jestSetupFileAfterEnv.ts @@ -1,6 +1 @@ import '@testing-library/jest-dom'; -// @TODO It would be nice to mock fetch also, but there are some issues with fetch-mock being esm only, while -// we are importing it from commonjs. We should investigate this further. -// import fetchMock from 'fetch-mock'; - -// global.fetch = fetchMock.sandbox() as typeof fetch; diff --git a/packages/testing-library/src/localization.ts b/packages/testing-library/src/localization.ts index a4a07b6c17..f457386580 100644 --- a/packages/testing-library/src/localization.ts +++ b/packages/testing-library/src/localization.ts @@ -1,9 +1,12 @@ import path from 'path'; +import { resolveImaConfig } from '@ima/cli'; +import type { ImaCliArgs } from '@ima/cli'; import { assignRecursively } from '@ima/helpers'; import MessageFormat from '@messageformat/core'; import globby from 'globby'; +import { getImaTestingLibraryClientConfig } from './configuration'; import { requireFromProject } from './helpers'; /** @@ -17,9 +20,9 @@ import { requireFromProject } from './helpers'; * @param {string} locale * @returns {object} */ -function generateDictionary(locale = 'cs') { - // @TODO: locale should be taken from config and should be en by default - const { languages } = requireFromProject('./ima.config.js'); +function generateDictionary(locale: string) { + const { rootDir } = getImaTestingLibraryClientConfig(); + const { languages } = resolveImaConfig({ rootDir } as ImaCliArgs); const mf = new MessageFormat(locale); const dictionaries: Record = {}; const langFileGlobs = languages[locale]; diff --git a/packages/testing-library/src/rtl.tsx b/packages/testing-library/src/rtl.tsx index 783abb02eb..ea29f7624a 100644 --- a/packages/testing-library/src/rtl.tsx +++ b/packages/testing-library/src/rtl.tsx @@ -1,19 +1,12 @@ -import path from 'node:path'; - import * as imaCore from '@ima/core'; import { PageContext } from '@ima/react-page-renderer'; import { render, RenderOptions } from '@testing-library/react'; // eslint-disable-line import/named import React, { ReactElement } from 'react'; -import { getImaTestingLibraryConfig } from './configuration'; +import { getImaTestingLibraryClientConfig } from './configuration'; import { requireFromProject } from './helpers'; import { generateDictionary } from './localization'; - -export interface ContextValue { - $Utils: imaCore.Utils; -} - -export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, 'app/main.js'); +import type { ContextValue } from './types'; // Some operations take way too long to be executed with each render call, // so we need to cache these values @@ -27,21 +20,33 @@ const mainFile: Record< const dictionary: Record = {}; export function initImaApp() { - const config = getImaTestingLibraryConfig(); + const config = getImaTestingLibraryClientConfig(); + + if (!document || !window) { + throw new Error( + 'Missing document, or window. Are you running the test in the jsdom environment?' + ); + } if (!mainFile[config.appMainPath]) { mainFile[config.appMainPath] = requireFromProject(config.appMainPath); } - if (!dictionary[config.locale]) { - dictionary[config.locale] = generateDictionary(config.locale); + if (!dictionary[$IMA.$Language]) { + if (!$IMA.$Language) { + throw new Error( + 'Variable $IMA.$Language is not defined. The variable should be defined in the jsdom html template, but it is missing. Maybe your SPA template is not setting this variable?' + ); + } + + dictionary[$IMA.$Language] = generateDictionary($IMA.$Language); } const { ima, getInitialAppConfigFunctions } = mainFile[config.appMainPath]; // Init language files // This must be initialized before oc.get('$Dictionary').init() is called (usualy part of initServices) - global.$IMA.i18n = dictionary[config.locale]; + $IMA.i18n = dictionary[$IMA.$Language]; const app = ima.createImaApp(); const bootConfig = ima.getClientBootConfig(getInitialAppConfigFunctions()); @@ -49,17 +54,21 @@ export function initImaApp() { // Init app ima.bootClientApp(app, bootConfig); + config.afterInitImaApp(app); + return app; } export function getContextValue( app?: ReturnType ): ContextValue { + const config = getImaTestingLibraryClientConfig(); + if (!app) { app = initImaApp(); } - return { $Utils: app.oc.get('$ComponentUtils').getUtils() }; + return config.getContextValue(app); } export function getContextWrapper(contextValue?: ContextValue) { diff --git a/packages/testing-library/src/server/config/environment.ts b/packages/testing-library/src/server/config/environment.ts new file mode 100644 index 0000000000..1d072ad516 --- /dev/null +++ b/packages/testing-library/src/server/config/environment.ts @@ -0,0 +1,137 @@ +import type { AppEnvironment } from '@ima/core'; +import type { Request } from 'express'; + +const environment: AppEnvironment = { + /** + * The production environment is used as a base template for other + * environment configurations. Meaning that all `dev` or `test` env + * definitions are deeply merged into `prod` base config. + * + * So you can only define other-env specific overrides without the need + * to re-define whole custom configuration. + */ + prod: { + /** + * Enable/disable debug mode. When enabled you can see additional + * error messages while this also enable some additional validation + * that can produce additional errors. + */ + $Debug: false, + $Version: '1.0.0', + /* + * Key-value pairs used for configuring the languages used with + * specific hosts or starting paths. + * + * - Key: Has to start with '//' instead of a protocol, and you can + * define the root path. Optional parameter ":language" + * could be defined at the end to display language in the + * URL. + * + * - Value: Language to use when the key is matched by the current + * URL. If the ":language" parameter is used, the language + * specified in this value is used as the default language + * when the path part specifying the language is not + * present in the current URL. + */ + $Language: { + '//*:*': 'en', + }, + $Server: { + /** + * When defined it overrides any other protocol and + * host settings in the urlParser hook. + */ + // protocol: 'https', + // host: 'localhost', + /** + * The port at which the server listens for incoming HTTP connections + */ + port: 3001, + /** + * Base path, which serves static files form the build folder, + * see https://imajs.io/cli/ima-config-js/#publicpath for more info. + * Used in staticPath middleware definition in app.js. + */ + staticPath: '/static', + /** + * The number of application instances (not threads) used to handle + * concurrent connections within a single thread. + */ + concurrency: 100, + /** + * When the number of concurrent connection exceeds the `staticConcurrency`, + * the server response with static files for 4xx and 5xx. + */ + staticConcurrency: 100, + /** + * When the number of concurrent connection exceeds the `overloadConcurrency`, + * the server response with 503 status code. + */ + overloadConcurrency: 100, + /** + * Define the number of server processes you want to start. + * Use `null` for the current number of available CPU cores. + */ + clusters: null, + /** + * SPA mode means, that the server-side-render is completely disabled + * and clients receive base template generated from spa.ejs file + * with app root html and static files, which initialize the app + * only on client-side. This negates some performance impacts of SSR + * on the app server. + */ + serveSPA: { + /** + * When enabled, and the number of concurrent connection exceeds the concurrency, + * the server will serve the application in SPA mode (without server-side rendering) + */ + allow: true, + /** + * These user agents will always be served a server-rendered page. + */ + blackList: (userAgent: string) => + new RegExp('Googlebot|SeznamBot').test(userAgent), + }, + cache: { + // boolean, or function(Express.Request): boolean + enabled: false, + // null or function(Express.Request): string + cacheKeyGenerator: undefined, + // the maximum time a cache entry is kept + entryTtl: 60 * 60 * 1000, // milliseconds + // the time after which the unused entries are discarded + unusedEntryTtl: 15 * 60 * 1000, // milliseconds + // the maximum entries in cache + maxEntries: 500, + }, + logger: { + /** + * Use "simple", "JSON" or "dev". "dev" option produces colorful output + * with source-mapping of error stacks. This is usefull in development. + */ + formatting: 'simple', + }, + }, + /** + * Options for 'express-http-proxy' defined in app.js file. It's purpose + * is mainly to create proxy to REST API server. This should be used only + * in development due to performance and possible security concerns. + */ + $Proxy: { + // Server route/path at which the proxy will be listening for + path: '/api', + // Proxy sever URL + server: 'example.com', + // Options to pass to the express-http-proxy + options: { + https: true, + timeout: 10000, + proxyReqPathResolver: (request: Request) => `/api/v1${request.url}`, + }, + }, + }, +}; + +const { prod } = environment; + +export { prod }; diff --git a/packages/testing-library/src/server/template/400.ejs b/packages/testing-library/src/server/template/400.ejs new file mode 100644 index 0000000000..aec7e4bc18 --- /dev/null +++ b/packages/testing-library/src/server/template/400.ejs @@ -0,0 +1,14 @@ + + + + + + + IMA.js - 400 + + + +

400 Page

+ + + diff --git a/packages/testing-library/src/server/template/500.ejs b/packages/testing-library/src/server/template/500.ejs new file mode 100644 index 0000000000..9ad3eaf696 --- /dev/null +++ b/packages/testing-library/src/server/template/500.ejs @@ -0,0 +1,14 @@ + + + + + + + IMA.js - 500 + + + +

500 Page

+ + + diff --git a/packages/testing-library/src/server/template/spa.ejs b/packages/testing-library/src/server/template/spa.ejs new file mode 100644 index 0000000000..d7b81a6ce6 --- /dev/null +++ b/packages/testing-library/src/server/template/spa.ejs @@ -0,0 +1,18 @@ + + + + + + + IMA.js - SPA + #{styles} + + + +
+ #{revivalCache} + #{revivalSettings} + #{runner} + + + diff --git a/packages/testing-library/src/types.ts b/packages/testing-library/src/types.ts new file mode 100644 index 0000000000..49a52b19a0 --- /dev/null +++ b/packages/testing-library/src/types.ts @@ -0,0 +1,5 @@ +import type { Utils } from '@ima/core'; + +export interface ContextValue { + $Utils: Utils; +} From a9ac2265f238ed773139db8646f51c0949691c0d Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 19 Aug 2024 13:27:12 +0200 Subject: [PATCH 03/17] fix: types and tests --- package-lock.json | 44 +------------------ .../__tests__/__snapshots__/CardSpec.js.snap | 41 +++++++++-------- .../app/page/home/__tests__/HomeViewSpec.js | 3 ++ .../src/presets/resolvers.ts | 6 +-- 4 files changed, 27 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74fb4d0211..c7021c8e6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2423,28 +2423,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@cfaester/enzyme-adapter-react-18": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@cfaester/enzyme-adapter-react-18/-/enzyme-adapter-react-18-0.7.1.tgz", - "integrity": "sha512-Z3m1qNSlQdrcXdnPSGOAysLdgJFSowu7sbK1cHRcWXuZGS3WOTFOS0kIXbWMa1FnkEbswlIU6KgS+8qKgM6Kqw==", - "dev": true, - "dependencies": { - "enzyme-shallow-equal": "^1.0.0", - "react-is": "^18.2.0", - "react-test-renderer": "^18.2.0" - }, - "peerDependencies": { - "enzyme": "^3.11.0", - "react": "^18.2.0", - "react-dom": "^18.2.0" - } - }, - "node_modules/@cfaester/enzyme-adapter-react-18/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, "node_modules/@changesets/apply-release-plan": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz", @@ -26366,26 +26344,6 @@ "react": "^16.8.0 || ^17.0.0-0 || ^18.0.0" } }, - "node_modules/react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", - "dev": true, - "dependencies": { - "react-is": "^18.2.0", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-test-renderer/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, "node_modules/react-textarea-autosize": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", @@ -34029,7 +33987,6 @@ "memoize-one": "^6.0.0" }, "devDependencies": { - "@cfaester/enzyme-adapter-react-18": "^0.7.0", "@testing-library/react": "^16.0.0", "@types/react": "^18.0.33", "@types/react-dom": "^18.0.6", @@ -34089,6 +34046,7 @@ } }, "packages/testing-library": { + "name": "@ima/testing-library", "version": "19.6.0", "license": "MIT", "peerDependencies": { diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap index f89b08f656..ad750143a4 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap @@ -1,27 +1,26 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Card can render 1`] = ` -
- +
-

- Test card - » -

-
-

+ +

+ Test card + » +

+ +

+ Some content of the card. +

+
`; diff --git a/packages/create-ima-app/template/common/app/page/home/__tests__/HomeViewSpec.js b/packages/create-ima-app/template/common/app/page/home/__tests__/HomeViewSpec.js index c916ca0f29..4c2b8496ec 100644 --- a/packages/create-ima-app/template/common/app/page/home/__tests__/HomeViewSpec.js +++ b/packages/create-ima-app/template/common/app/page/home/__tests__/HomeViewSpec.js @@ -1,3 +1,6 @@ +/** + * @jest-environment node + */ import { initImaApp, clearImaApp } from '@ima/plugin-testing-integration'; import cards from '../../../public/cards.json'; diff --git a/packages/storybook-integration/src/presets/resolvers.ts b/packages/storybook-integration/src/presets/resolvers.ts index 72999dc053..23466d548a 100644 --- a/packages/storybook-integration/src/presets/resolvers.ts +++ b/packages/storybook-integration/src/presets/resolvers.ts @@ -107,16 +107,16 @@ export function resolveRevivalSettings({ process.env.NODE_ENV = oldEnv; const revivalSettings = `(function (root) { - root.$Debug = ${env.$App.$Debug}; + root.$Debug = ${env.$App?.$Debug}; root.$IMA = root.$IMA || {}; $IMA.Test = true; $IMA.SPA = true; - $IMA.$App = ${JSON.stringify(env.$App)}; + $IMA.$App = ${JSON.stringify(env.$App || {})}; $IMA.$PublicPath = ""; $IMA.$RequestID = "storybook-request-id"; $IMA.$Language = "${options.language ?? 'en'}"; $IMA.$Env = "regression"; - $IMA.$Debug = ${env.$App.$Debug}; + $IMA.$Debug = ${env.$App?.$Debug}; $IMA.$Version = "${env.$Version}"; $IMA.$Protocol = "${options.https ? 'https:' : 'http:'}"; $IMA.$Host = "${ From 52ca33be6fd145cc0499e63140daf057d1a9f76a Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 19 Aug 2024 13:31:53 +0200 Subject: [PATCH 04/17] test: add missing dependency in create-ima-app test --- utils/tests/testRunner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/tests/testRunner.sh b/utils/tests/testRunner.sh index 81596ab7c2..efd3e4f31f 100755 --- a/utils/tests/testRunner.sh +++ b/utils/tests/testRunner.sh @@ -17,7 +17,7 @@ cd "$ROOT_DIR_IMA" CREATE_IMA_APP_DIR="$ROOT_DIR_IMA/packages/create-ima-app" PACKAGE_VERSION="0.0.0-next" -PACKAGES="cli core create-ima-app dev-utils error-overlay helpers hmr-client server react-page-renderer" +PACKAGES="cli core create-ima-app dev-utils error-overlay helpers hmr-client server react-page-renderer testing-library" # Setup local registry node_modules/.bin/verdaccio -l "$NPM_LOCAL_REGISTRY_URL_NO_PROTOCOL" -c utils/tests/verdaccio_config.yml >/dev/null & From 80dd396b7351104273137bc4819b562a0b1291e2 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 23 Aug 2024 14:27:40 +0200 Subject: [PATCH 05/17] feat: more isolated client/server sides to prevent import errors --- packages/cli/src/webpack/utils.ts | 2 +- packages/react-page-renderer/jest.config.js | 6 +- .../src/hooks/__tests__/componentSpec.js | 6 +- packages/testing-library/README.md | 172 ++++++++++++++---- packages/testing-library/package.json | 4 + packages/testing-library/src/configuration.ts | 61 +------ packages/testing-library/src/helpers.ts | 6 +- packages/testing-library/src/jest-preset.ts | 92 ++++------ packages/testing-library/src/localization.ts | 101 +++++++--- packages/testing-library/src/rtl.tsx | 127 +++++++------ .../src/server/configuration.ts | 48 +++++ .../testing-library/src/server/content.ts | 51 ++++++ packages/testing-library/src/server/index.ts | 2 + packages/testing-library/src/types.ts | 10 +- packages/testing-library/tsconfig.json | 3 + utils/tests/createImaAppTests.sh | 3 + utils/tests/testRunner.sh | 3 - 17 files changed, 448 insertions(+), 249 deletions(-) create mode 100644 packages/testing-library/src/server/configuration.ts create mode 100644 packages/testing-library/src/server/content.ts create mode 100644 packages/testing-library/src/server/index.ts diff --git a/packages/cli/src/webpack/utils.ts b/packages/cli/src/webpack/utils.ts index dd68a4065f..9ff1d6e7d5 100644 --- a/packages/cli/src/webpack/utils.ts +++ b/packages/cli/src/webpack/utils.ts @@ -226,7 +226,7 @@ export function requireImaConfig(rootDir = process.cwd()): ImaConfig | null { * @param {ImaCliArgs} args CLI args. * @returns {Promise} Ima config or empty object. */ -export function resolveImaConfig(args: ImaCliArgs): ImaConfig { +export async function resolveImaConfig(args: ImaCliArgs): Promise { const defaultImaConfig: ImaConfig = { publicPath: '/', compress: true, diff --git a/packages/react-page-renderer/jest.config.js b/packages/react-page-renderer/jest.config.js index 18974c00f4..a2d01a34ac 100644 --- a/packages/react-page-renderer/jest.config.js +++ b/packages/react-page-renderer/jest.config.js @@ -1,7 +1,8 @@ const { setImaTestingLibraryServerConfig, + FALLBACK_APP_MAIN_PATH, FALLBACK_APPLICATION_FOLDER, -} = require('@ima/testing-library'); +} = require('@ima/testing-library/server'); const defaultConfig = require('../../jest.config.base.js'); @@ -12,5 +13,8 @@ setImaTestingLibraryServerConfig({ module.exports = { ...defaultConfig, preset: '@ima/testing-library', + moduleNameMapper: { + 'app/main': FALLBACK_APP_MAIN_PATH, + }, testRegex: '(/__tests__/).*Spec\\.[jt]s$', }; diff --git a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js index cd8d70e51d..c5c076a19d 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js @@ -1,4 +1,4 @@ -import { render } from '@ima/testing-library'; +import { renderWithContext } from '@ima/testing-library'; import { renderHook } from '../../testUtils'; import { useComponent, useOnce } from '../component'; @@ -29,7 +29,7 @@ describe('useComponent', () => { }); describe('useOnce', () => { - it('should call callback only once', () => { + it('should call callback only once', async () => { let count = 0; const TestComponent = () => { @@ -38,7 +38,7 @@ describe('useOnce', () => { return null; }; - const { rerender } = render(); + const { rerender } = await renderWithContext(); rerender(); rerender(); diff --git a/packages/testing-library/README.md b/packages/testing-library/README.md index 272a848d78..95b22a4e19 100644 --- a/packages/testing-library/README.md +++ b/packages/testing-library/README.md @@ -41,15 +41,19 @@ In this case, you need the jest config file to be in non-json format. This configuration should be evaluated in the jest config file. It's config values are used to initialize the JSDOM environment in which the tests are running. ```javascript -const { setImaTestingLibraryServerConfig } = require('@ima/testing-library'); +const { setImaTestingLibraryServerConfig, FALLBACK_APPLICATION_FOLDER, FALLBACK_APP_MAIN_PATH } = require('@ima/testing-library/server'); setImaTestingLibraryServerConfig({ // your custom config - applicationFolder: '/path/to/folder/containing/server/folder', + applicationFolder: FALLBACK_APPLICATION_FOLDER, // There is a default application folder as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real application folder with server files. }); module.exports = { - preset: '@ima/testing-library' + preset: '@ima/testing-library', + // The preset automatically sets up the moduleNameMapper for the IMA.js application, but you can override it if you need to. + moduleNameMapper: { + 'app/main': FALLBACK_APP_MAIN_PATH, // There is a default app main file as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real app main file. + } }; ``` @@ -62,67 +66,167 @@ const { setImaTestingLibraryClientConfig, FALLBACK_APP_MAIN_PATH } = require('@i setImaTestingLibraryClientConfig({ // your custom config - appMainPath: FALLBACK_APP_MAIN_PATH, // There is a default app main file as part of the package, it contains only the minimal setup and it might be enough for you if you don't have any real app main file. + appMainPath: FALLBACK_APP_MAIN_PATH, imaConfigPath: 'path/to/your/ima.config.js', }); ``` ## Usage -IMA Testing Library is re-exporting everything from `@testing-library/react`. It provides the default context wrapper for the `render` method. Thanks to this, the default example from the React Testing Library documentation will work out of the box. You just need to import the `render` method from the `@ima/testing-library` package. +IMA Testing Library is re-exporting everything from `@testing-library/react`. You should always import React Testing Library functions from `@ima/testing-library` as we might add some additional functionality / wrappers in the future. + +IMA Testing Library exports async function `renderWithContext`. It adds default context to the render function from RTL. The context is created from the IMA.js application and it contains the same values as the real application context. ```javascript -import { render } from '@ima/testing-library'; +import { renderWithContext } from '@ima/testing-library'; -test('renders learn react link', () => { - const { getByText } = render(); - const linkElement = getByText(/learn react/i); +test('renders learn react link', async () => { + const { getByText } = await renderWithContext(My Text); + const textElement = getByText(/My Text/i); - expect(linkElement).toBeInTheDocument(); + expect(textElement).toBeInTheDocument(); }); ``` You might need to specify custom additions to the context, or mock some parts of the IMA application. You can do this by providing a custom context wrapper and using the `@ima/testing-library` specific utilities. ```javascript -import { render, getContextWrapper, getContextValue, initImaApp } from '@ima/testing-library'; +import { renderWithContext, getContextValue, initImaApp } from '@ima/testing-library'; -test('renders learn react link with custom context wrapper', () => { - const ContextWrapper = getContextWrapper(); - const { getByText } = render(, { - wrapper: , - }); - const linkElement = getByText(/learn react/i); +test('renders learn react link with custom app configuration', async () => { + const app = await initImaApp(); + + app.oc.get('$Utils').$Foo = jest.fn(() => 'bar'); + + const { getByText } = await renderWithContext(My Text, { app }); + const textElement = getByText(/My Text/i); - expect(linkElement).toBeInTheDocument(); + expect(textElement).toBeInTheDocument(); }); -test('renders learn react link with custom context value', () => { - const contextValue = getContextValue(); +test('renders learn react link with custom context value', async () => { + const app = await initImaApp(); + const contextValue = await getContextValue(app); contextValue.$Utils.$Foo = jest.fn(() => 'bar'); - const ContextWrapper = getContextWrapper(contextValue); - const { getByText } = render(, { - wrapper: ContextWrapper, + const { getByText } = await renderWithContext(My Text, { contextValue }); + const textElement = getByText(/My Text/i); + + expect(textElement).toBeInTheDocument(); +}); +``` + +### Extending IMA boot config methods + +You can extend IMA boot config by using [IMA `pluginLoader.register`](https://imajs.io/api/classes/ima_core.PluginLoader/#register) method. Use the same approach as in IMA plugins. + +You can either register a plugin loader for all tests by setting it up in a setup file. + +```javascript +// jestSetup.js +import { pluginLoader } from '@ima/core'; + +// If you don't care, if this plugin loader is registered first, or last +pluginLoader.register('jestSetup.js', () => { + return { + initSettings: () => { + return { + prod: { + customSetting: 'customValue' + } + } + } + }; +}); + +// If you need to register the plugin loader as the last one +beforeAll(() => { + pluginLoader.register('jestSetup.js', () => { + return { + initSettings: () => { + return { + prod: { + customSetting: 'customValue' + } + } + } + }; }); - const linkElement = getByText(/learn react/i); +}); + +// jest.config.js +module.exports = { + // Add this line to your jest config + setupFilesAfterEnv: ['./jestSetup.js'] +}; +``` - expect(linkElement).toBeInTheDocument(); +Or you can register a plugin loader for a specific test file. + +```javascript +// mySpec.js +import { pluginLoader } from '@ima/core'; + +beforeAll(() => { + pluginLoader.register('mySpec', () => { + return { + initSettings: () => { + return { + prod: { + customSetting: 'customValue' + } + } + } + }; + }); }); -test('renders learn react link with custom app configuration', () => { - const app = initImaApp(); +test('renders learn react link with custom app configuration', async () => { + const { getByText } = await renderWithContext(My Text); + const textElement = getByText(/My Text/i); - app.oc.get('$Utils').$Foo = jest.fn(() => 'bar'); + expect(textElement).toBeInTheDocument(); +}); +``` - const contextValue = getContextValue(app); - const ContextWrapper = getContextWrapper(contextValue); - const { getByText } = render(, { - wrapper: ContextWrapper, +Or you can register a plugin loader for a test file, but make the boot config methods dynamic so you can change them for each test. + +```javascript +// mySpec.js +import { pluginLoader } from '@ima/core'; + +// We create a placeholder for the plugin loader, so we can change it later +let initSettings = () => {}; + +beforeAll(() => { + pluginLoader.register('mySpec', (...args) => { + return { + initSettings: (...args) => { + return initSettings(...args); + } + }; }); - const linkElement = getByText(/learn react/i); +}); + +afterEach(() => { + initSettings = () => {}; // Reset the plugin loader +}); + +test('renders learn react link with custom app configuration', async () => { + initSettings = () => { + return { + prod: { + customSetting: 'customValue' + } + } + }; - expect(linkElement).toBeInTheDocument(); + const { getByText } = await renderWithContext(My Text); + const textElement = getByText(/My Text/i); + + expect(textElement).toBeInTheDocument(); }); ``` + +*Note, that the plugin loader register method evaluates the second argument right away, but the specific boot config methods are evaluated during `renderWithContext` (or `initImaApp` if you are using it directly).* diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index b9933fc41b..bdb98ac820 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -31,6 +31,10 @@ "import": "./dist/esm/index.js", "default": "./dist/cjs/index.js" }, + "./server": { + "import": "./dist/esm/server/index.js", + "default": "./dist/cjs/server/index.js" + }, "./jest-preset": "./dist/cjs/jest-preset.js", "./jestSetupFileAfterEnv": "./dist/cjs/jestSetupFileAfterEnv.js" }, diff --git a/packages/testing-library/src/configuration.ts b/packages/testing-library/src/configuration.ts index befec4b147..ed715d8400 100644 --- a/packages/testing-library/src/configuration.ts +++ b/packages/testing-library/src/configuration.ts @@ -1,33 +1,10 @@ -import path from 'node:path'; - -import type { createImaApp, Environment } from '@ima/core'; - -import type { ContextValue } from './types'; - -export interface ServerConfiguration { - /** - * The protocol of the application. - */ - protocol: string; - /** - * The host of the application. - */ - host: string; - /** - * The process environment configuration. This allows you to change the environment configuration that will be available in jsdom. - */ - processEnvironment: (env: Environment) => Environment; - /** - * The path to the application folder. - */ - applicationFolder: string | undefined; -} +import type { ContextValue, ImaApp } from './types'; export interface ClientConfiguration { /** - * The path to the main application file. This file should be exporting getInitialAppConfigFunctions and ima keys. + * If true, the fake dictionary will be used. The real dictionary degrades the performance of the tests so disable this with caution. */ - appMainPath: string; + useFakeDictionary: boolean; /** * The path to the IMA configuration file. This can be only configured once before first `initImaApp` call and cannot be reconfigured later. */ @@ -35,22 +12,15 @@ export interface ClientConfiguration { /** * The function that will be called after the IMA application is initialized. */ - afterInitImaApp: (app: ReturnType) => void; + afterInitImaApp: (app: ImaApp) => void; /** * The function that will be called after the context value is created. */ - getContextValue: (app: ReturnType) => ContextValue; + getContextValue: (app: ImaApp) => ContextValue; } -const serverConfiguration: ServerConfiguration = { - protocol: 'https:', - host: 'imajs.io', - processEnvironment: env => env, - applicationFolder: undefined, -}; - const clientConfiguration: ClientConfiguration = { - appMainPath: 'app/main.js', + useFakeDictionary: true, rootDir: process.cwd(), afterInitImaApp: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function getContextValue: app => ({ @@ -58,25 +28,6 @@ const clientConfiguration: ClientConfiguration = { }), }; -export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, 'app/main.js'); -export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname); - -/** - * Get the current serverConfiguration. - */ -export function getImaTestingLibraryServerConfig() { - return serverConfiguration; -} - -/** - * Modify the current serverConfiguration. - */ -export function setImaTestingLibraryServerConfig( - config: Partial -) { - Object.assign(serverConfiguration, config); -} - /** * Get the current clientConfiguration. */ diff --git a/packages/testing-library/src/helpers.ts b/packages/testing-library/src/helpers.ts index 40f856acec..c904221221 100644 --- a/packages/testing-library/src/helpers.ts +++ b/packages/testing-library/src/helpers.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; /** * Requires specified file from projectPath @@ -6,6 +6,6 @@ import path from 'path'; * @param {string} projectPath relative project path to a file * @returns {*} File exports */ -export function requireFromProject(projectPath: string) { - return require(path.resolve(projectPath)); +export function importFromProject(projectPath: string) { + return import(path.resolve(projectPath)); } diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts index 15f3bc7f08..7e0f1299e5 100644 --- a/packages/testing-library/src/jest-preset.ts +++ b/packages/testing-library/src/jest-preset.ts @@ -1,69 +1,43 @@ -import { createIMAServer } from '@ima/server'; import type { Config } from 'jest'; -import { getImaTestingLibraryServerConfig } from './configuration'; - -const serverConfig = getImaTestingLibraryServerConfig(); - -/** - * Get response content from @ima/server. - */ -async function _getIMAResponseContent(): Promise { - // Mock devUtils to override manifest loading - const devUtils = { - manifestRequire: () => ({}), - }; - - // Prepare serverApp with environment override - const { serverApp } = await createIMAServer({ - devUtils, - applicationFolder: serverConfig.applicationFolder, - processEnvironment: currentEnvironment => - serverConfig.processEnvironment({ - ...currentEnvironment, - $Server: { - ...currentEnvironment.$Server, - concurrency: 0, - serveSPA: { - allow: true, - }, - }, - $Debug: true, - }), - }); - - // Generate request response - const response = await serverApp.requestHandler( - { - get: () => '', - headers: () => '', - originalUrl: serverConfig.host, - protocol: serverConfig.protocol.replace(':', ''), - }, - { - status: () => 200, - send: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function - set: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function - locals: {}, - } - ); - - return response.content; -} +import { + getIMAResponseContent, + getImaTestingLibraryServerConfig, +} from './server'; /** * Jest configuration for IMA testing library. * We are entering undocumented territory here, jestConfig is a promise, but documentation does not mention, if it is allowed. * It would be nice if there was a synchronous and more straightforward way of generating IMA SPA content. */ -const jestConfig: Promise = (async () => ({ - setupFiles: ['@ima/core/setupJest.js'], - setupFilesAfterEnv: ['@ima/testing-library/jestSetupFileAfterEnv'], - testEnvironment: 'jsdom', - testEnvironmentOptions: { - html: await _getIMAResponseContent(), - url: `${serverConfig.protocol}//${serverConfig.host}/`, - }, -}))(); +const jestConfig: Promise = (async () => { + const serverConfig = getImaTestingLibraryServerConfig(); + + let html; + + try { + html = await getIMAResponseContent(); + } catch (error: any) { + // Some async errors are swallowed by jest, so we need to log them manually and throw a safe error + console.error(error.stack ?? error); + + throw new Error( + 'Failed to get IMA response content. Check the error above.' + ); + } + + return { + setupFiles: ['@ima/core/setupJest.js'], + setupFilesAfterEnv: ['@ima/testing-library/jestSetupFileAfterEnv'], + moduleNameMapper: { + 'app/main': '/app/main', + }, + testEnvironment: 'jsdom', + testEnvironmentOptions: { + html, + url: `${serverConfig.protocol}//${serverConfig.host}/`, + }, + }; +})(); export default jestConfig; diff --git a/packages/testing-library/src/localization.ts b/packages/testing-library/src/localization.ts index f457386580..006308519a 100644 --- a/packages/testing-library/src/localization.ts +++ b/packages/testing-library/src/localization.ts @@ -1,18 +1,67 @@ -import path from 'path'; +/** + * @TODO The localization logic should be mostly taken from https://github.com/seznam/ima/blob/master/packages/cli/src/webpack/languages.ts + * This solution is similar, but not the same and there can be some inconsistencies. + */ +import path from 'node:path'; import { resolveImaConfig } from '@ima/cli'; import type { ImaCliArgs } from '@ima/cli'; +import type { DictionaryConfig, DictionaryData } from '@ima/core'; import { assignRecursively } from '@ima/helpers'; import MessageFormat from '@messageformat/core'; import globby from 'globby'; import { getImaTestingLibraryClientConfig } from './configuration'; -import { requireFromProject } from './helpers'; +import { importFromProject } from './helpers'; + +// Some operations take way too long to be executed with each render call, +// so we need to cache these values +const dictionary: Record = {}; /** - * @TODO The localization logic should be mostly taken from https://github.com/seznam/ima/blob/master/packages/cli/src/webpack/languages.ts - * This solution is similar, but not the same and there can be some inconsistencies. + * Generates dictionary object, either fake or real depending on the configuration. + */ +export async function generateDictionary() { + const config = getImaTestingLibraryClientConfig(); + + if (config.useFakeDictionary) { + $IMA.i18n = generateFakeDictionary(); + } else { + if (!dictionary[$IMA.$Language]) { + if (!$IMA.$Language) { + throw new Error( + 'Variable $IMA.$Language is not defined. The variable should be defined in the jsdom html template, but it is missing. Maybe your SPA template is not setting this variable?' + ); + } + + dictionary[$IMA.$Language] = await generateRealDictionary($IMA.$Language); + } + + $IMA.i18n = dictionary[$IMA.$Language]; + } +} + +/** + * Generates infinite object as a fake dictionary, returning string `localize(key)` for every key. + * This function does not validate, if the key would exist in the real dictionary. + * Example: + * ``` + * const dictionary = generateFakeDictionary(); + * dictionary['key1']() === 'localize(key1)'; + * dictionary['key1']['key2']['key3']() === 'localize(key1.key2.key3)'; + * ``` */ +export function generateFakeDictionary(path = ''): DictionaryData { + return new Proxy(() => `localize(${path})`, { + get: (target, prop: string) => { + if (typeof prop === 'string') { + const newPath = path ? `${path}.${prop}` : prop; + return generateFakeDictionary(newPath); + } + return target[prop]; + }, + }) as unknown as DictionaryData; +} /** * Generates IMA formatted dictionary @@ -20,32 +69,36 @@ import { requireFromProject } from './helpers'; * @param {string} locale * @returns {object} */ -function generateDictionary(locale: string) { +export async function generateRealDictionary(locale: string) { const { rootDir } = getImaTestingLibraryClientConfig(); - const { languages } = resolveImaConfig({ rootDir } as ImaCliArgs); + const { languages } = await resolveImaConfig({ rootDir } as ImaCliArgs); const mf = new MessageFormat(locale); const dictionaries: Record = {}; const langFileGlobs = languages[locale]; - globby.sync(langFileGlobs).forEach(file => { - try { - const filename = path - .basename(file) - .replace(locale.toUpperCase() + path.extname(file), ''); + await Promise.all( + ( + await globby(langFileGlobs) + ).map(async file => { + try { + const filename = path + .basename(file) + .replace(locale.toUpperCase() + path.extname(file), ''); - const dictJson = requireFromProject(file); + const dictJson = await importFromProject(file); - dictionaries[filename] = assignRecursively( - dictionaries[filename] ?? {}, - _deepMapValues(dictJson, mf.compile.bind(mf)) - ); - } catch (e) { - console.error( - `Tried to load dictionary JSON at path "${file}", but recieved following error.` - ); - console.error(e); - } - }); + dictionaries[filename] = assignRecursively( + dictionaries[filename] ?? {}, + _deepMapValues(dictJson, mf.compile.bind(mf)) + ); + } catch (e) { + console.error( + `Tried to load dictionary JSON at path "${file}", but recieved following error.` + ); + console.error(e); + } + }) + ); return dictionaries; } @@ -79,5 +132,3 @@ function _deepMapValues( return fn(obj); } } - -export { generateDictionary }; diff --git a/packages/testing-library/src/rtl.tsx b/packages/testing-library/src/rtl.tsx index ea29f7624a..757205ab71 100644 --- a/packages/testing-library/src/rtl.tsx +++ b/packages/testing-library/src/rtl.tsx @@ -1,105 +1,104 @@ -import * as imaCore from '@ima/core'; import { PageContext } from '@ima/react-page-renderer'; -import { render, RenderOptions } from '@testing-library/react'; // eslint-disable-line import/named -import React, { ReactElement } from 'react'; +import { render } from '@testing-library/react'; +import type { RenderOptions } from '@testing-library/react'; +import { ReactElement } from 'react'; + +// import of app/main is resolved by the jest moduleNameMapper +import { ima, getInitialAppConfigFunctions } from 'app/main'; import { getImaTestingLibraryClientConfig } from './configuration'; -import { requireFromProject } from './helpers'; import { generateDictionary } from './localization'; -import type { ContextValue } from './types'; - -// Some operations take way too long to be executed with each render call, -// so we need to cache these values -const mainFile: Record< - string, - { - ima: typeof imaCore; - getInitialAppConfigFunctions: () => imaCore.InitAppConfig; - } -> = {}; -const dictionary: Record = {}; - -export function initImaApp() { - const config = getImaTestingLibraryClientConfig(); - +import type { + ContextValue, + ImaApp, + ImaContextWrapper, + ImaRenderResult, +} from './types'; + +/** + * Initialize the IMA application for testing. + */ +export async function initImaApp(): Promise { if (!document || !window) { throw new Error( 'Missing document, or window. Are you running the test in the jsdom environment?' ); } - if (!mainFile[config.appMainPath]) { - mainFile[config.appMainPath] = requireFromProject(config.appMainPath); - } - - if (!dictionary[$IMA.$Language]) { - if (!$IMA.$Language) { - throw new Error( - 'Variable $IMA.$Language is not defined. The variable should be defined in the jsdom html template, but it is missing. Maybe your SPA template is not setting this variable?' - ); - } - - dictionary[$IMA.$Language] = generateDictionary($IMA.$Language); - } - - const { ima, getInitialAppConfigFunctions } = mainFile[config.appMainPath]; + const config = getImaTestingLibraryClientConfig(); // Init language files // This must be initialized before oc.get('$Dictionary').init() is called (usualy part of initServices) - $IMA.i18n = dictionary[$IMA.$Language]; + await generateDictionary(); - const app = ima.createImaApp(); - const bootConfig = ima.getClientBootConfig(getInitialAppConfigFunctions()); + const app = await ima.createImaApp(); + const bootConfig = await ima.getClientBootConfig( + await getInitialAppConfigFunctions() + ); // Init app - ima.bootClientApp(app, bootConfig); + await ima.bootClientApp(app, bootConfig); - config.afterInitImaApp(app); + await config.afterInitImaApp(app); return app; } -export function getContextValue( - app?: ReturnType -): ContextValue { +/** + * Get the context value for the IMA application. + */ +export async function getContextValue(app?: ImaApp): Promise { const config = getImaTestingLibraryClientConfig(); if (!app) { - app = initImaApp(); + app = await initImaApp(); } return config.getContextValue(app); } -export function getContextWrapper(contextValue?: ContextValue) { - if (!contextValue) { - contextValue = getContextValue(); - } - - return function IMATestingLibraryContextWrapper({ - children, - }: { - children: React.ReactNode; - }) { +/** + * Creates a context wrapper component from the provided context value. + */ +async function getContextWrapper( + contextValue: ContextValue +): Promise { + return function IMATestingLibraryContextWrapper({ children }) { return ( - + {children} ); }; } -const customRender = (ui: ReactElement, options?: RenderOptions) => { - let { wrapper, ...rest } = options ?? {}; // eslint-disable-line prefer-const +/** + * Renders the provided React element with the IMA context. + */ +async function renderWithContext( + ui: ReactElement, + options?: RenderOptions & { contextValue?: ContextValue; app?: ImaApp } +): Promise { + let { app = null, contextValue, ...rest } = options ?? {}; // eslint-disable-line prefer-const + + if (!contextValue) { + if (!app) { + app = await initImaApp(); + } - if (!wrapper) { - wrapper = getContextWrapper(); + contextValue = await getContextValue(app); } - const result = render(ui, { wrapper, ...rest }); + const wrapper = await getContextWrapper(contextValue); + + const result = render(ui, { ...rest, wrapper }); - return result; -}; + return { + ...result, + app, + contextValue, + }; +} -export * from '@testing-library/react'; // eslint-disable-line import/export -export { customRender as render }; // eslint-disable-line import/export +export * from '@testing-library/react'; +export { renderWithContext }; diff --git a/packages/testing-library/src/server/configuration.ts b/packages/testing-library/src/server/configuration.ts new file mode 100644 index 0000000000..b1084935c5 --- /dev/null +++ b/packages/testing-library/src/server/configuration.ts @@ -0,0 +1,48 @@ +import path from 'node:path'; + +import type { Environment } from '@ima/core'; + +export interface ServerConfiguration { + /** + * The protocol of the application. + */ + protocol: string; + /** + * The host of the application. + */ + host: string; + /** + * The process environment configuration. This allows you to change the environment configuration that will be available in jsdom. + */ + processEnvironment: (env: Environment) => Environment; + /** + * The path to the application folder. + */ + applicationFolder: string | undefined; +} + +const serverConfiguration: ServerConfiguration = { + protocol: 'https:', + host: 'imajs.io', + processEnvironment: env => env, + applicationFolder: undefined, +}; + +export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, '../app/main.js'); +export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname, '..'); + +/** + * Get the current serverConfiguration. + */ +export function getImaTestingLibraryServerConfig() { + return serverConfiguration; +} + +/** + * Modify the current serverConfiguration. + */ +export function setImaTestingLibraryServerConfig( + config: Partial +) { + Object.assign(serverConfiguration, config); +} diff --git a/packages/testing-library/src/server/content.ts b/packages/testing-library/src/server/content.ts new file mode 100644 index 0000000000..96bb866e80 --- /dev/null +++ b/packages/testing-library/src/server/content.ts @@ -0,0 +1,51 @@ +import { createIMAServer } from '@ima/server'; + +import { getImaTestingLibraryServerConfig } from './configuration'; + +const serverConfig = getImaTestingLibraryServerConfig(); + +/** + * Get response content from @ima/server. + */ +export async function getIMAResponseContent(): Promise { + // Mock devUtils to override manifest loading + const devUtils = { + manifestRequire: () => ({}), + }; + + // Prepare serverApp with environment override + const { serverApp } = await createIMAServer({ + devUtils, + applicationFolder: serverConfig.applicationFolder, + processEnvironment: currentEnvironment => + serverConfig.processEnvironment({ + ...currentEnvironment, + $Server: { + ...currentEnvironment.$Server, + concurrency: 0, + serveSPA: { + allow: true, + }, + }, + $Debug: true, + }), + }); + + // Generate request response + const response = await serverApp.requestHandler( + { + get: () => '', + headers: () => '', + originalUrl: serverConfig.host, + protocol: serverConfig.protocol.replace(':', ''), + }, + { + status: () => 200, + send: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + set: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function + locals: {}, + } + ); + + return response.content; +} diff --git a/packages/testing-library/src/server/index.ts b/packages/testing-library/src/server/index.ts new file mode 100644 index 0000000000..bd7d3007d3 --- /dev/null +++ b/packages/testing-library/src/server/index.ts @@ -0,0 +1,2 @@ +export * from './configuration'; +export * from './content'; diff --git a/packages/testing-library/src/types.ts b/packages/testing-library/src/types.ts index 49a52b19a0..23c3930437 100644 --- a/packages/testing-library/src/types.ts +++ b/packages/testing-library/src/types.ts @@ -1,5 +1,13 @@ -import type { Utils } from '@ima/core'; +import type { Utils, createImaApp } from '@ima/core'; +import { render } from '@testing-library/react'; export interface ContextValue { $Utils: Utils; } + +export type ImaApp = ReturnType; +export type ImaContextWrapper = React.FC<{ children: React.ReactNode }>; +export type ImaRenderResult = ReturnType & { + app: ImaApp | null; + contextValue: ContextValue; +}; diff --git a/packages/testing-library/tsconfig.json b/packages/testing-library/tsconfig.json index 00b55540fd..212cd01ad0 100644 --- a/packages/testing-library/tsconfig.json +++ b/packages/testing-library/tsconfig.json @@ -9,6 +9,9 @@ "DOM.Iterable", ], "outDir": "dist", + "paths": { + "app/main": ["./src/app/main"], + }, "types": ["webpack", "core", "cli", "react-page-renderer", "less-plugin-glob"] }, "include": [ diff --git a/utils/tests/createImaAppTests.sh b/utils/tests/createImaAppTests.sh index abe1ed7b91..6a3ed0538e 100755 --- a/utils/tests/createImaAppTests.sh +++ b/utils/tests/createImaAppTests.sh @@ -1,5 +1,8 @@ #!/bin/bash +TARGET_WEB_URL="http://localhost:3001/" +PARALLEL_TEST_CONNECTIONS=300 + # Add customized environment configuration mv server/config/environment.js server/config/environment.orig.js cp "$ROOT_DIR_IMA/utils/tests/app/environment.js" server/config/environment.js diff --git a/utils/tests/testRunner.sh b/utils/tests/testRunner.sh index efd3e4f31f..ddc21b828a 100755 --- a/utils/tests/testRunner.sh +++ b/utils/tests/testRunner.sh @@ -2,9 +2,6 @@ set -e -TARGET_WEB_URL="http://localhost:3001/" -PARALLEL_TEST_CONNECTIONS=300 -SKELETON_URL="https://github.com/seznam/IMA.js-skeleton.git" NPM_LOCAL_REGISTRY_URL_NO_PROTOCOL="localhost:4873" NPM_LOCAL_REGISTRY_URL="http://${NPM_LOCAL_REGISTRY_URL_NO_PROTOCOL}/" ROOT_DIR_IMA=`pwd` From 40557a927b5efc63835d6da5c628b37799d5fd62 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 23 Aug 2024 14:44:24 +0200 Subject: [PATCH 06/17] fix: cleanup --- packages/create-ima-app/template/common/jest.config.js | 1 - packages/testing-library/README.md | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/create-ima-app/template/common/jest.config.js b/packages/create-ima-app/template/common/jest.config.js index 893f50cc76..595f4ad7f7 100644 --- a/packages/create-ima-app/template/common/jest.config.js +++ b/packages/create-ima-app/template/common/jest.config.js @@ -2,7 +2,6 @@ module.exports = { bail: true, preset: '@ima/testing-library', modulePaths: ['/'], - setupFiles: ['@ima/core/setupJest.js'], testRegex: '(/__tests__/).*Spec\\.jsx?$', moduleNameMapper: { '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': diff --git a/packages/testing-library/README.md b/packages/testing-library/README.md index 95b22a4e19..13cb4021de 100644 --- a/packages/testing-library/README.md +++ b/packages/testing-library/README.md @@ -10,7 +10,7 @@ ## IMA Testing Library -The `@ima/testing-library` contains utilities for testing IMA.js applications. It provides integration with [Jest](https://jestjs.io), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) (RTL for short) amd [Testing Library Jest DOM](https://testing-library.com/docs/ecosystem-jest-dom). +The `@ima/testing-library` contains utilities for testing IMA.js applications. It provides integration with [Jest](https://jestjs.io), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) (RTL for short) and [Testing Library Jest DOM](https://testing-library.com/docs/ecosystem-jest-dom). ## Installation @@ -75,7 +75,7 @@ setImaTestingLibraryClientConfig({ IMA Testing Library is re-exporting everything from `@testing-library/react`. You should always import React Testing Library functions from `@ima/testing-library` as we might add some additional functionality / wrappers in the future. -IMA Testing Library exports async function `renderWithContext`. It adds default context to the render function from RTL. The context is created from the IMA.js application and it contains the same values as the real application context. +IMA Testing Library exports async function `renderWithContext`. It adds default context to the `render` function from RTL. The context is created from the IMA.js application and it contains the same values as the real application context. ```javascript import { renderWithContext } from '@ima/testing-library'; @@ -203,14 +203,14 @@ beforeAll(() => { pluginLoader.register('mySpec', (...args) => { return { initSettings: (...args) => { - return initSettings(...args); + return initSettings(...args); // Here we call our overridable function } }; }); }); afterEach(() => { - initSettings = () => {}; // Reset the plugin loader + initSettings = () => {}; // Reset the plugin loader so it is not called for other tests }); test('renders learn react link with custom app configuration', async () => { From c0abac8071a97a1b2d3d38ab6afc11384dcdd4e6 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 23 Aug 2024 14:53:44 +0200 Subject: [PATCH 07/17] feat: add swc_mut_cjs_exports --- jest.config.base.js | 6 + package-lock.json | 597 +++--------------- package.json | 3 +- .../template/common/jest.config.js | 6 + .../template/common/package.json | 3 +- 5 files changed, 117 insertions(+), 498 deletions(-) diff --git a/jest.config.base.js b/jest.config.base.js index ae581fba2f..9bcf7ad933 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -8,6 +8,9 @@ module.exports = { '@swc/jest', { jsc: { + experimental: { + plugins: [['swc_mut_cjs_exports', {}]], + }, target: 'es2022', parser: { syntax: 'ecmascript', @@ -25,6 +28,9 @@ module.exports = { '@swc/jest', { jsc: { + experimental: { + plugins: [['swc_mut_cjs_exports', {}]], + }, target: 'es2022', parser: { syntax: 'typescript', diff --git a/package-lock.json b/package-lock.json index c7021c8e6f..34c9613949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,6 +84,7 @@ "stylelint-declaration-block-no-ignored-properties": "^2.6.0", "stylelint-order": "^6.0.2", "stylelint-prettier": "^3.0.0", + "swc_mut_cjs_exports": "^0.99.0", "to-mock": "1.6.2", "typescript": "^5.0.3", "verdaccio": "^5.23.2", @@ -5037,23 +5038,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", @@ -5101,33 +5085,17 @@ } } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/create-cache-key-function": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz", - "integrity": "sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1" + "@jest/types": "^29.6.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/environment": { @@ -5145,23 +5113,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", @@ -5204,23 +5155,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", @@ -5236,23 +5170,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", @@ -5296,23 +5213,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5373,23 +5273,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/test-sequencer": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", @@ -5431,23 +5314,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/transform/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5455,28 +5321,20 @@ "dev": true }, "node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^16.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/types/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -7529,12 +7387,14 @@ } }, "node_modules/@swc/core": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.84.tgz", - "integrity": "sha512-UPKUiDwG7HOdPfOb1VFeEJ76JDgU2w80JLewzx6tb0fk9TIjhr9yxKBzPbzc/QpjGHDu5iaEuNeZcu27u4j63g==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.14.tgz", + "integrity": "sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow==", "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@swc/types": "^0.1.4" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" }, "engines": { "node": ">=10" @@ -7544,19 +7404,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.84", - "@swc/core-darwin-x64": "1.3.84", - "@swc/core-linux-arm-gnueabihf": "1.3.84", - "@swc/core-linux-arm64-gnu": "1.3.84", - "@swc/core-linux-arm64-musl": "1.3.84", - "@swc/core-linux-x64-gnu": "1.3.84", - "@swc/core-linux-x64-musl": "1.3.84", - "@swc/core-win32-arm64-msvc": "1.3.84", - "@swc/core-win32-ia32-msvc": "1.3.84", - "@swc/core-win32-x64-msvc": "1.3.84" + "@swc/core-darwin-arm64": "1.7.14", + "@swc/core-darwin-x64": "1.7.14", + "@swc/core-linux-arm-gnueabihf": "1.7.14", + "@swc/core-linux-arm64-gnu": "1.7.14", + "@swc/core-linux-arm64-musl": "1.7.14", + "@swc/core-linux-x64-gnu": "1.7.14", + "@swc/core-linux-x64-musl": "1.7.14", + "@swc/core-win32-arm64-msvc": "1.7.14", + "@swc/core-win32-ia32-msvc": "1.7.14", + "@swc/core-win32-x64-msvc": "1.7.14" }, "peerDependencies": { - "@swc/helpers": "^0.5.0" + "@swc/helpers": "*" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -7565,12 +7425,13 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.84.tgz", - "integrity": "sha512-mqK0buOo+toF2HoJ/gWj2ApZbvbIiNq3mMwSTHCYJHlQFQfoTWnl9aaD5GSO4wfNFVYfEZ1R259o5uv5NlVtoA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz", + "integrity": "sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ==", "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -7580,12 +7441,13 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.84.tgz", - "integrity": "sha512-cyuQZz62C43EDZqtnptUTlfDvAjgG3qu139m5zsfIK6ltXA5inKFbDWV3a/M5c18dFzA2Xh21Q46XZezmtQ9Tg==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.14.tgz", + "integrity": "sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ==", "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -7595,12 +7457,13 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.84.tgz", - "integrity": "sha512-dmt/ECQrp3ZPWnK27p4E4xRIRHOoJhgGvxC5t5YaWzN20KcxE9ykEY2oLGSoeceM/A+4D11aRYGwF/EM7yOkvA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.14.tgz", + "integrity": "sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA==", "cpu": [ "arm" ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -7610,12 +7473,13 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.84.tgz", - "integrity": "sha512-PgVfrI3NVg2z/oeg3GWLb9rFLMqidbdPwVH5nRyHVP2RX/BWP6qfnYfG+gJv4qrKzIldb9TyCGH7y8VWctKLxw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.14.tgz", + "integrity": "sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g==", "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -7625,12 +7489,13 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.84.tgz", - "integrity": "sha512-hcuEa8/vin4Ns0P+FpcDHQ4f3jmhgGKQhqw0w+TovPSVTIXr+nrFQ2AGhs9nAxS6tSQ77C53Eb5YRpK8ToFo1A==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.14.tgz", + "integrity": "sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ==", "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -7640,12 +7505,13 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.84.tgz", - "integrity": "sha512-IvyimSbwGdu21jBBEqR1Up8Jhvl8kIAf1k3e5Oy8oRfgojdUfmW1EIwgGdoUeyQ1VHlfquiWaRGfsnHQUKl35g==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.14.tgz", + "integrity": "sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og==", "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -7655,12 +7521,13 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.84.tgz", - "integrity": "sha512-hdgVU/O5ufDCe+p5RtCjU7PRNwd0WM+eWJS+GNY4QWL6O8y2VLM+i4+6YzwSUjeBk0xd+1YElMxbqz7r5tSZhw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.14.tgz", + "integrity": "sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ==", "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -7670,12 +7537,13 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.84.tgz", - "integrity": "sha512-rzH6k2BF0BFOFhUTD+bh0oCiUCZjFfDfoZoYNN/CM0qbtjAcFH21hzMh/EH8ZaXq8k/iQmUNNa5MPNPZ4SOMNw==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.14.tgz", + "integrity": "sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A==", "cpu": [ "arm64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -7685,12 +7553,13 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.84.tgz", - "integrity": "sha512-Y+Dk7VLLVwwsAzoDmjkNW/sTmSPl9PGr4Mj1nhc5A2NNxZ+hz4SxFMclacDI03SC5ikK8Qh6WOoE/+nwUDa3uA==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.14.tgz", + "integrity": "sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA==", "cpu": [ "ia32" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -7700,12 +7569,13 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.84.tgz", - "integrity": "sha512-WmpaosqCWMX7DArLdU8AJcj96hy0PKlYh1DaMVikSrrDHbJm2dZ8rd27IK3qUB8DgPkrDYHmLAKNZ+z3gWXgRQ==", + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz", + "integrity": "sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q==", "cpu": [ "x64" ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -7714,13 +7584,21 @@ "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, "node_modules/@swc/jest": { - "version": "0.2.29", - "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.29.tgz", - "integrity": "sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==", + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.36.tgz", + "integrity": "sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/create-cache-key-function": "^27.4.2", + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", "jsonc-parser": "^3.2.0" }, "engines": { @@ -7731,9 +7609,13 @@ } }, "node_modules/@swc/types": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.4.tgz", - "integrity": "sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", @@ -12679,23 +12561,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/cross-argv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/cross-argv/-/cross-argv-2.0.0.tgz", @@ -19127,23 +18992,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-circus/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -19192,23 +19040,6 @@ } } }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", @@ -19254,23 +19085,6 @@ } } }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -19334,23 +19148,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-environment-jsdom": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", @@ -19378,23 +19175,6 @@ } } }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-environment-jsdom/node_modules/cssstyle": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", @@ -19514,23 +19294,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -19565,23 +19328,6 @@ "fsevents": "^2.3.2" } }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", @@ -19630,23 +19376,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", @@ -19661,23 +19390,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", @@ -19769,23 +19481,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runner/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -19834,23 +19529,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runtime/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -19902,23 +19580,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-snapshot/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -19968,22 +19629,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", @@ -20001,23 +19646,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -20197,23 +19825,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", @@ -20242,23 +19853,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jiti": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", @@ -30093,6 +29687,17 @@ "node": ">= 10" } }, + "node_modules/swc_mut_cjs_exports": { + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/swc_mut_cjs_exports/-/swc_mut_cjs_exports-0.99.0.tgz", + "integrity": "sha512-v3/AiUXrM/6ELCUo7zDHszpYYK0iFmGsnFSAUTsqGFkLH6rFq055L14Wg95agAhwDxO1M7e3nQbxsIUbFv4Khg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@swc/core": "^1.7.0", + "@swc/jest": "^0.2.36" + } + }, "node_modules/swc-loader": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.3.tgz", diff --git a/package.json b/package.json index e1a3fce2ba..744d208b50 100644 --- a/package.json +++ b/package.json @@ -76,8 +76,8 @@ "copy-webpack-plugin": "^11.0.0", "core-js": "^3.24.0", "css-loader": "^6.7.1", - "es-check": "^7.1.0", "css-minimizer-webpack-plugin": "^5.0.0", + "es-check": "^7.1.0", "eslint": "^8.27.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.5.3", @@ -113,6 +113,7 @@ "stylelint-declaration-block-no-ignored-properties": "^2.6.0", "stylelint-order": "^6.0.2", "stylelint-prettier": "^3.0.0", + "swc_mut_cjs_exports": "^0.99.0", "to-mock": "1.6.2", "typescript": "^5.0.3", "verdaccio": "^5.23.2", diff --git a/packages/create-ima-app/template/common/jest.config.js b/packages/create-ima-app/template/common/jest.config.js index 595f4ad7f7..e1e9b19487 100644 --- a/packages/create-ima-app/template/common/jest.config.js +++ b/packages/create-ima-app/template/common/jest.config.js @@ -13,6 +13,9 @@ module.exports = { '@swc/jest', { jsc: { + experimental: { + plugins: [['swc_mut_cjs_exports', {}]], + }, parser: { syntax: 'ecmascript', jsx: true, @@ -29,6 +32,9 @@ module.exports = { '@swc/jest', { jsc: { + experimental: { + plugins: [['swc_mut_cjs_exports', {}]], + }, parser: { syntax: 'typescript', tsx: true, diff --git a/packages/create-ima-app/template/common/package.json b/packages/create-ima-app/template/common/package.json index bce5143abf..7efa7de419 100644 --- a/packages/create-ima-app/template/common/package.json +++ b/packages/create-ima-app/template/common/package.json @@ -33,7 +33,8 @@ "identity-obj-proxy": "^3.0.0", "jest": "^29.3.1", "jest-environment-jsdom": "^29.7.0", - "prettier": "^2.6.0" + "prettier": "^2.6.0", + "swc_mut_cjs_exports": "^0.99.0" }, "dependencies": { "@ima/core": "^19.0.3", From 03d109ad6a4f042b463316b440de297508793267 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 23 Aug 2024 15:55:50 +0200 Subject: [PATCH 08/17] doc: add changesets --- .changeset/clean-pandas-move.md | 5 +++++ .changeset/mighty-shrimps-repair.md | 7 +++++++ .changeset/soft-frogs-reply.md | 5 +++++ .gitignore | 1 + 4 files changed, 18 insertions(+) create mode 100644 .changeset/clean-pandas-move.md create mode 100644 .changeset/mighty-shrimps-repair.md create mode 100644 .changeset/soft-frogs-reply.md diff --git a/.changeset/clean-pandas-move.md b/.changeset/clean-pandas-move.md new file mode 100644 index 0000000000..139bd026b1 --- /dev/null +++ b/.changeset/clean-pandas-move.md @@ -0,0 +1,5 @@ +--- +"create-ima-app": minor +--- + +Use `@ima/testing-library` instead of `enzyme` by default. diff --git a/.changeset/mighty-shrimps-repair.md b/.changeset/mighty-shrimps-repair.md new file mode 100644 index 0000000000..646d1daed0 --- /dev/null +++ b/.changeset/mighty-shrimps-repair.md @@ -0,0 +1,7 @@ +--- +"@ima/storybook-integration": patch +"@ima/server": patch +"@ima/core": patch +--- + +Fix some typing errors. diff --git a/.changeset/soft-frogs-reply.md b/.changeset/soft-frogs-reply.md new file mode 100644 index 0000000000..95e799b83d --- /dev/null +++ b/.changeset/soft-frogs-reply.md @@ -0,0 +1,5 @@ +--- +"@ima/testing-library": minor +--- + +Init new module, see README.md for more info. diff --git a/.gitignore b/.gitignore index b3f16393e1..a397926a66 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ website/.turbo *.tgz .vscode yarn.lock +.swc/ From a7b46781542fe801dc55a98eba28aa2908a10bbc Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 23 Aug 2024 15:56:34 +0200 Subject: [PATCH 09/17] fix: add .swc to gitignore --- packages/create-ima-app/template/common/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-ima-app/template/common/.gitignore b/packages/create-ima-app/template/common/.gitignore index ad59241b2f..be0e6986e8 100644 --- a/packages/create-ima-app/template/common/.gitignore +++ b/packages/create-ima-app/template/common/.gitignore @@ -12,3 +12,4 @@ build/ app/assets/bower .npmrc package-lock.json +.swc/ From f15d759e09632ac57269e0a690803668ffca8289 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 2 Sep 2024 12:27:17 +0200 Subject: [PATCH 10/17] feat: add ITL support for RTL v12+ --- .../app/component/card/__tests__/CardSpec.js | 2 +- .../__tests__/__snapshots__/CardSpec.js.snap | 38 +++++++++---------- packages/testing-library/package.json | 2 +- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js index 470845c97f..3267184a55 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js @@ -12,6 +12,6 @@ describe('Card', () => { const { container } = render(); - expect(container).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); }); diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap index ad750143a4..d84088a670 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap @@ -1,26 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Card can render 1`] = ` -
-
+ - -

- Test card - » -

-
-

- Some content of the card. -

-
+

+ Test card + » +

+ +

+ Some content of the card. +

`; diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index bdb98ac820..dcdec33b99 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -43,7 +43,7 @@ "@ima/react-page-renderer": ">=19.0.0", "@testing-library/dom": ">=10.0.0", "@testing-library/jest-dom": ">=6.0.0", - "@testing-library/react": ">=16.0.0" + "@testing-library/react": ">=12.0.0" }, "publishConfig": { "access": "public", From 764ca6adbb2cbf4dbc919288a87b9e43b7304ca4 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 2 Sep 2024 13:06:05 +0200 Subject: [PATCH 11/17] feat: added fallback config autodetection --- .../__tests__/__snapshots__/CardSpec.js.snap | 2 +- packages/react-page-renderer/jest.config.js | 13 ---------- packages/react-page-renderer/setupJest.js | 8 ------ packages/testing-library/README.md | 10 ++++---- packages/testing-library/src/jest-preset.ts | 8 +++++- .../src/server/configuration.ts | 25 +++++++++++++------ 6 files changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap index d84088a670..5f9d74f188 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap @@ -12,7 +12,7 @@ exports[`Card can render 1`] = ` >

Test card - » + »

/app/main.test.js', // You can tell jest to use a different main file for the tests } }; ``` @@ -62,11 +63,10 @@ module.exports = { This configuration should be evaluated in the setup files, or directly in the test files. It's config values are used to initialize the IMA.js application and provide the context for the tests. ```javascript -const { setImaTestingLibraryClientConfig, FALLBACK_APP_MAIN_PATH } = require('@ima/testing-library'); +const { setImaTestingLibraryClientConfig } = require('@ima/testing-library'); setImaTestingLibraryClientConfig({ // your custom config - appMainPath: FALLBACK_APP_MAIN_PATH, imaConfigPath: 'path/to/your/ima.config.js', }); ``` diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts index 7e0f1299e5..2d438b318e 100644 --- a/packages/testing-library/src/jest-preset.ts +++ b/packages/testing-library/src/jest-preset.ts @@ -1,8 +1,12 @@ +import fs from 'node:fs'; +import path from 'node:path'; + import type { Config } from 'jest'; import { getIMAResponseContent, getImaTestingLibraryServerConfig, + FALLBACK_APP_MAIN_PATH, } from './server'; /** @@ -30,7 +34,9 @@ const jestConfig: Promise = (async () => { setupFiles: ['@ima/core/setupJest.js'], setupFilesAfterEnv: ['@ima/testing-library/jestSetupFileAfterEnv'], moduleNameMapper: { - 'app/main': '/app/main', + 'app/main': fs.existsSync(path.resolve('./app/main.js')) + ? '/app/main' + : FALLBACK_APP_MAIN_PATH, }, testEnvironment: 'jsdom', testEnvironmentOptions: { diff --git a/packages/testing-library/src/server/configuration.ts b/packages/testing-library/src/server/configuration.ts index b1084935c5..fc3ba494ee 100644 --- a/packages/testing-library/src/server/configuration.ts +++ b/packages/testing-library/src/server/configuration.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs'; import path from 'node:path'; import type { Environment } from '@ima/core'; @@ -21,16 +22,11 @@ export interface ServerConfiguration { applicationFolder: string | undefined; } -const serverConfiguration: ServerConfiguration = { - protocol: 'https:', - host: 'imajs.io', - processEnvironment: env => env, - applicationFolder: undefined, -}; - export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, '../app/main.js'); export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname, '..'); +const serverConfiguration: ServerConfiguration = resolveDefaultConfiguration(); + /** * Get the current serverConfiguration. */ @@ -46,3 +42,18 @@ export function setImaTestingLibraryServerConfig( ) { Object.assign(serverConfiguration, config); } + +function resolveDefaultConfiguration() { + const serverConfiguration: ServerConfiguration = { + protocol: 'https:', + host: 'imajs.io', + processEnvironment: env => env, + applicationFolder: undefined, + }; + + if (!fs.existsSync(path.resolve('.', 'server/config/environment.js'))) { + serverConfiguration.applicationFolder = FALLBACK_APPLICATION_FOLDER; + } + + return serverConfiguration; +} From 0582409c5963a008094f598093c5762f319afcd4 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Mon, 2 Sep 2024 13:17:55 +0200 Subject: [PATCH 12/17] test: fix snapshot --- .../app/component/card/__tests__/__snapshots__/CardSpec.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap index 5f9d74f188..ea0d57ed8f 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/__snapshots__/CardSpec.js.snap @@ -12,7 +12,7 @@ exports[`Card can render 1`] = ` >

Test card - » + »

Date: Fri, 6 Sep 2024 12:15:30 +0200 Subject: [PATCH 13/17] fix: add possibility to set config without importing whole project beforehand --- .../common/app/component/card/__tests__/CardSpec.js | 6 +++--- .../src/hooks/__tests__/componentSpec.js | 13 +++++++------ packages/testing-library/README.md | 4 ++-- packages/testing-library/package.json | 12 ++++++++++++ .../src/{ => client}/app/config/bind.ts | 0 .../src/{ => client}/app/config/settings.ts | 0 .../testing-library/src/{ => client}/app/main.ts | 0 .../src/{ => client}/configuration.ts | 2 +- packages/testing-library/src/client/index.ts | 11 +++++++++++ packages/testing-library/src/index.ts | 1 - packages/testing-library/src/jest-preset.ts | 2 +- packages/testing-library/src/localization.ts | 2 +- packages/testing-library/src/rtl.tsx | 2 +- .../testing-library/src/server/configuration.ts | 5 ++++- packages/testing-library/tsconfig.json | 2 +- 15 files changed, 44 insertions(+), 18 deletions(-) rename packages/testing-library/src/{ => client}/app/config/bind.ts (100%) rename packages/testing-library/src/{ => client}/app/config/settings.ts (100%) rename packages/testing-library/src/{ => client}/app/main.ts (100%) rename packages/testing-library/src/{ => client}/configuration.ts (95%) create mode 100644 packages/testing-library/src/client/index.ts diff --git a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js index 3267184a55..66f70cc5f9 100644 --- a/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js +++ b/packages/create-ima-app/template/common/app/component/card/__tests__/CardSpec.js @@ -1,16 +1,16 @@ -import { render } from '@ima/testing-library'; +import { renderWithContext } from '@ima/testing-library'; import { Card } from '../Card'; describe('Card', () => { - it('can render', () => { + it('can render', async () => { const props = { title: 'Test card', href: 'https://www.seznam.cz', children: 'Some content of the card.', }; - const { container } = render(); + const { container } = await renderWithContext(); expect(container.firstChild).toMatchSnapshot(); }); diff --git a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js index c5c076a19d..ee0cdb561c 100644 --- a/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js +++ b/packages/react-page-renderer/src/hooks/__tests__/componentSpec.js @@ -35,16 +35,17 @@ describe('useOnce', () => { const TestComponent = () => { useOnce(() => count++); - return null; + return

NotEmpty
; }; - const { rerender } = await renderWithContext(); + const { container, rerender } = await renderWithContext(); - rerender(); - rerender(); - rerender(); - rerender(); + rerender(); + rerender(); + rerender(); + rerender(); + expect(container).not.toBeEmptyDOMElement(); expect(count).toBe(1); }); }); diff --git a/packages/testing-library/README.md b/packages/testing-library/README.md index d459c2cf0c..15c5f8aa76 100644 --- a/packages/testing-library/README.md +++ b/packages/testing-library/README.md @@ -53,7 +53,7 @@ module.exports = { preset: '@ima/testing-library', // The preset automatically sets up the moduleNameMapper for the IMA.js application, but you can override it if you need to. moduleNameMapper: { - 'app/main': '/app/main.test.js', // You can tell jest to use a different main file for the tests + '^app/main$': '/app/main.test.js', // You can tell jest to use a different main file for the tests } }; ``` @@ -63,7 +63,7 @@ module.exports = { This configuration should be evaluated in the setup files, or directly in the test files. It's config values are used to initialize the IMA.js application and provide the context for the tests. ```javascript -const { setImaTestingLibraryClientConfig } = require('@ima/testing-library'); +const { setImaTestingLibraryClientConfig } = require('@ima/testing-library/client'); setImaTestingLibraryClientConfig({ // your custom config diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index dcdec33b99..d286aeff70 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -31,10 +31,22 @@ "import": "./dist/esm/index.js", "default": "./dist/cjs/index.js" }, + "./client": { + "import": "./dist/esm/client/index.js", + "default": "./dist/cjs/client/index.js" + }, "./server": { "import": "./dist/esm/server/index.js", "default": "./dist/cjs/server/index.js" }, + "./fallback/app/main": { + "import": "./dist/esm/client/app/main.js", + "default": "./dist/cjs/client/app/main.js" + }, + "./fallback/config/environment": { + "import": "./dist/esm/server/config/environment.js", + "default": "./dist/cjs/server/config/environment.js" + }, "./jest-preset": "./dist/cjs/jest-preset.js", "./jestSetupFileAfterEnv": "./dist/cjs/jestSetupFileAfterEnv.js" }, diff --git a/packages/testing-library/src/app/config/bind.ts b/packages/testing-library/src/client/app/config/bind.ts similarity index 100% rename from packages/testing-library/src/app/config/bind.ts rename to packages/testing-library/src/client/app/config/bind.ts diff --git a/packages/testing-library/src/app/config/settings.ts b/packages/testing-library/src/client/app/config/settings.ts similarity index 100% rename from packages/testing-library/src/app/config/settings.ts rename to packages/testing-library/src/client/app/config/settings.ts diff --git a/packages/testing-library/src/app/main.ts b/packages/testing-library/src/client/app/main.ts similarity index 100% rename from packages/testing-library/src/app/main.ts rename to packages/testing-library/src/client/app/main.ts diff --git a/packages/testing-library/src/configuration.ts b/packages/testing-library/src/client/configuration.ts similarity index 95% rename from packages/testing-library/src/configuration.ts rename to packages/testing-library/src/client/configuration.ts index ed715d8400..0e4e7904b3 100644 --- a/packages/testing-library/src/configuration.ts +++ b/packages/testing-library/src/client/configuration.ts @@ -1,4 +1,4 @@ -import type { ContextValue, ImaApp } from './types'; +import type { ContextValue, ImaApp } from '../types'; export interface ClientConfiguration { /** diff --git a/packages/testing-library/src/client/index.ts b/packages/testing-library/src/client/index.ts new file mode 100644 index 0000000000..21eb56d839 --- /dev/null +++ b/packages/testing-library/src/client/index.ts @@ -0,0 +1,11 @@ +/** + * Exposed as @ima/testing-library/client + * + * The separation from @ima/testing-library is necessary to avoid importing `app/main` when + * users just want to adjust the configuration in a jest setup file. The `app/main` file can + * import a lot of unnecessary dependencies and slow down the test suite. Also, it can break some + * `jest.mock` calls as any dependency imported from jest setup file is evaluated before `jest.mock` calls + * in the test file. + */ + +export * from './configuration'; diff --git a/packages/testing-library/src/index.ts b/packages/testing-library/src/index.ts index 81fa8a2bd6..9b302c9f43 100644 --- a/packages/testing-library/src/index.ts +++ b/packages/testing-library/src/index.ts @@ -1,3 +1,2 @@ export * from './rtl'; -export * from './configuration'; export * from './types'; diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts index 2d438b318e..44e875a83f 100644 --- a/packages/testing-library/src/jest-preset.ts +++ b/packages/testing-library/src/jest-preset.ts @@ -34,7 +34,7 @@ const jestConfig: Promise = (async () => { setupFiles: ['@ima/core/setupJest.js'], setupFilesAfterEnv: ['@ima/testing-library/jestSetupFileAfterEnv'], moduleNameMapper: { - 'app/main': fs.existsSync(path.resolve('./app/main.js')) + '^app/main$': fs.existsSync(path.resolve('./app/main.js')) ? '/app/main' : FALLBACK_APP_MAIN_PATH, }, diff --git a/packages/testing-library/src/localization.ts b/packages/testing-library/src/localization.ts index 006308519a..a8041e4f5d 100644 --- a/packages/testing-library/src/localization.ts +++ b/packages/testing-library/src/localization.ts @@ -11,7 +11,7 @@ import { assignRecursively } from '@ima/helpers'; import MessageFormat from '@messageformat/core'; import globby from 'globby'; -import { getImaTestingLibraryClientConfig } from './configuration'; +import { getImaTestingLibraryClientConfig } from './client/configuration'; import { importFromProject } from './helpers'; // Some operations take way too long to be executed with each render call, diff --git a/packages/testing-library/src/rtl.tsx b/packages/testing-library/src/rtl.tsx index 757205ab71..4469d96022 100644 --- a/packages/testing-library/src/rtl.tsx +++ b/packages/testing-library/src/rtl.tsx @@ -6,7 +6,7 @@ import { ReactElement } from 'react'; // import of app/main is resolved by the jest moduleNameMapper import { ima, getInitialAppConfigFunctions } from 'app/main'; -import { getImaTestingLibraryClientConfig } from './configuration'; +import { getImaTestingLibraryClientConfig } from './client/configuration'; import { generateDictionary } from './localization'; import type { ContextValue, diff --git a/packages/testing-library/src/server/configuration.ts b/packages/testing-library/src/server/configuration.ts index fc3ba494ee..f657a61776 100644 --- a/packages/testing-library/src/server/configuration.ts +++ b/packages/testing-library/src/server/configuration.ts @@ -22,7 +22,10 @@ export interface ServerConfiguration { applicationFolder: string | undefined; } -export const FALLBACK_APP_MAIN_PATH = path.resolve(__dirname, '../app/main.js'); +export const FALLBACK_APP_MAIN_PATH = path.resolve( + __dirname, + '../client/app/main.js' +); export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname, '..'); const serverConfiguration: ServerConfiguration = resolveDefaultConfiguration(); diff --git a/packages/testing-library/tsconfig.json b/packages/testing-library/tsconfig.json index 212cd01ad0..28bfe9b14c 100644 --- a/packages/testing-library/tsconfig.json +++ b/packages/testing-library/tsconfig.json @@ -10,7 +10,7 @@ ], "outDir": "dist", "paths": { - "app/main": ["./src/app/main"], + "app/main": ["./src/client/app/main"], }, "types": ["webpack", "core", "cli", "react-page-renderer", "less-plugin-glob"] }, From 9625a1283a109de3cd93af95e78d92a2f7da86bd Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 6 Sep 2024 12:32:28 +0200 Subject: [PATCH 14/17] fix: tweak fallback configuration --- packages/testing-library/package.json | 2 +- packages/testing-library/src/jest-preset.ts | 3 +-- packages/testing-library/src/server/configuration.ts | 4 ---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index d286aeff70..41f4c2a21a 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -43,7 +43,7 @@ "import": "./dist/esm/client/app/main.js", "default": "./dist/cjs/client/app/main.js" }, - "./fallback/config/environment": { + "./fallback/server/config/environment": { "import": "./dist/esm/server/config/environment.js", "default": "./dist/cjs/server/config/environment.js" }, diff --git a/packages/testing-library/src/jest-preset.ts b/packages/testing-library/src/jest-preset.ts index 44e875a83f..87a42b004d 100644 --- a/packages/testing-library/src/jest-preset.ts +++ b/packages/testing-library/src/jest-preset.ts @@ -6,7 +6,6 @@ import type { Config } from 'jest'; import { getIMAResponseContent, getImaTestingLibraryServerConfig, - FALLBACK_APP_MAIN_PATH, } from './server'; /** @@ -36,7 +35,7 @@ const jestConfig: Promise = (async () => { moduleNameMapper: { '^app/main$': fs.existsSync(path.resolve('./app/main.js')) ? '/app/main' - : FALLBACK_APP_MAIN_PATH, + : '@ima/testing-library/fallback/app/main', }, testEnvironment: 'jsdom', testEnvironmentOptions: { diff --git a/packages/testing-library/src/server/configuration.ts b/packages/testing-library/src/server/configuration.ts index f657a61776..91f1932a2d 100644 --- a/packages/testing-library/src/server/configuration.ts +++ b/packages/testing-library/src/server/configuration.ts @@ -22,10 +22,6 @@ export interface ServerConfiguration { applicationFolder: string | undefined; } -export const FALLBACK_APP_MAIN_PATH = path.resolve( - __dirname, - '../client/app/main.js' -); export const FALLBACK_APPLICATION_FOLDER = path.resolve(__dirname, '..'); const serverConfiguration: ServerConfiguration = resolveDefaultConfiguration(); From 969b3ba1810aaca92caaa07eefd5d7c772824ae7 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 6 Sep 2024 13:00:39 +0200 Subject: [PATCH 15/17] fix: module exports optimization --- package-lock.json | 29 +++++++------------ package.json | 3 ++ .../template/common/package.json | 2 +- packages/testing-library/.npmignore | 1 - packages/testing-library/package.json | 18 ++++++++---- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 34c9613949..48c29c35b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,9 @@ "@rollup/plugin-node-resolve": "^15.0.2", "@size-limit/preset-big-lib": "^8.0.1", "@swc/jest": "^0.2.23", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^16.0.0", "@types/jest": "^29.2.2", "@types/node": "^18.7.13", "@typescript-eslint/eslint-plugin": "^5.57.1", @@ -106,8 +109,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@algolia/autocomplete-core": { "version": "1.9.3", @@ -7634,7 +7636,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -7653,7 +7654,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -7665,7 +7665,6 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7680,7 +7679,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", "license": "MIT", - "peer": true, "dependencies": { "@adobe/css-tools": "^4.4.0", "@babel/runtime": "^7.9.2", @@ -7702,7 +7700,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7715,8 +7712,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@testing-library/react": { "version": "16.0.0", @@ -7765,8 +7761,7 @@ "node_modules/@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "peer": true + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" }, "node_modules/@types/babel__core": { "version": "7.20.1", @@ -9709,7 +9704,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -12827,8 +12821,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cssdb": { "version": "7.7.2", @@ -14068,8 +14061,7 @@ "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "peer": true + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -21229,7 +21221,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -33652,14 +33643,14 @@ }, "packages/testing-library": { "name": "@ima/testing-library", - "version": "19.6.0", + "version": "19.7.0", "license": "MIT", "peerDependencies": { "@ima/core": ">=19.0.0", "@ima/react-page-renderer": ">=19.0.0", "@testing-library/dom": ">=10.0.0", "@testing-library/jest-dom": ">=6.0.0", - "@testing-library/react": ">=16.0.0" + "@testing-library/react": ">=12.0.0" } }, "website": { diff --git a/package.json b/package.json index 744d208b50..0b7e16e7ed 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,9 @@ "@rollup/plugin-node-resolve": "^15.0.2", "@size-limit/preset-big-lib": "^8.0.1", "@swc/jest": "^0.2.23", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^16.0.0", "@types/jest": "^29.2.2", "@types/node": "^18.7.13", "@typescript-eslint/eslint-plugin": "^5.57.1", diff --git a/packages/create-ima-app/template/common/package.json b/packages/create-ima-app/template/common/package.json index 7efa7de419..3b7898ce3d 100644 --- a/packages/create-ima-app/template/common/package.json +++ b/packages/create-ima-app/template/common/package.json @@ -19,7 +19,7 @@ "@babel/preset-react": "^7.16.7", "@ima/cli": "^19.0.1", "@ima/plugin-testing-integration": "4.1.0", - "@ima/testing-library": "../github-ima/ima-testing-library-19.6.0.tgz", + "@ima/testing-library": "^19.8.0", "@swc/jest": "^0.2.20", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.4.8", diff --git a/packages/testing-library/.npmignore b/packages/testing-library/.npmignore index 00d1f7e29d..b948e13ae2 100644 --- a/packages/testing-library/.npmignore +++ b/packages/testing-library/.npmignore @@ -1,4 +1,3 @@ * !dist/**/* !package.json -!jest-preset.js diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index 41f4c2a21a..a3b4fa17d5 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -1,6 +1,6 @@ { "name": "@ima/testing-library", - "version": "19.6.0", + "version": "19.7.0", "description": "Testing library for IMA.js applications.", "keywords": [ "IMA.js", @@ -43,12 +43,18 @@ "import": "./dist/esm/client/app/main.js", "default": "./dist/cjs/client/app/main.js" }, - "./fallback/server/config/environment": { - "import": "./dist/esm/server/config/environment.js", - "default": "./dist/cjs/server/config/environment.js" + "./fallback/server/*": { + "import": "./dist/esm/server/*.js", + "default": "./dist/cjs/server/*.js" }, - "./jest-preset": "./dist/cjs/jest-preset.js", - "./jestSetupFileAfterEnv": "./dist/cjs/jestSetupFileAfterEnv.js" + "./jest-preset": { + "import": "./dist/esm/jest-preset.js", + "default": "./dist/cjs/jest-preset.js" + }, + "./jestSetupFileAfterEnv": { + "import": "./dist/esm/jestSetupFileAfterEnv.js", + "default": "./dist/cjs/jestSetupFileAfterEnv.js" + } }, "peerDependencies": { "@ima/core": ">=19.0.0", From a345c2e8da0c5435fc300c6f26907a540eb663d4 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 6 Sep 2024 15:37:31 +0200 Subject: [PATCH 16/17] fix: types exports --- packages/testing-library/package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index a3b4fa17d5..ff54b9728f 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -32,26 +32,32 @@ "default": "./dist/cjs/index.js" }, "./client": { + "types": "./dist/esm/client/index.d.ts", "import": "./dist/esm/client/index.js", "default": "./dist/cjs/client/index.js" }, "./server": { + "types": "./dist/esm/server/index.d.ts", "import": "./dist/esm/server/index.js", "default": "./dist/cjs/server/index.js" }, "./fallback/app/main": { + "types": "./dist/esm/client/app/main.d.ts", "import": "./dist/esm/client/app/main.js", "default": "./dist/cjs/client/app/main.js" }, "./fallback/server/*": { + "types": "./dist/esm/server/*.d.ts", "import": "./dist/esm/server/*.js", "default": "./dist/cjs/server/*.js" }, "./jest-preset": { + "types": "./dist/esm/jest-preset.d.ts", "import": "./dist/esm/jest-preset.js", "default": "./dist/cjs/jest-preset.js" }, "./jestSetupFileAfterEnv": { + "types": "./dist/esm/jestSetupFileAfterEnv.d.ts", "import": "./dist/esm/jestSetupFileAfterEnv.js", "default": "./dist/cjs/jestSetupFileAfterEnv.js" } From a17b9bef5341f5f9dc520512f543a95034194e70 Mon Sep 17 00:00:00 2001 From: Filip Satek Date: Fri, 6 Sep 2024 16:19:32 +0200 Subject: [PATCH 17/17] fix: missing dependencies --- packages/testing-library/package.json | 9 ++++++++- packages/testing-library/src/rtl.tsx | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/testing-library/package.json b/packages/testing-library/package.json index ff54b9728f..27144f1c02 100644 --- a/packages/testing-library/package.json +++ b/packages/testing-library/package.json @@ -62,12 +62,19 @@ "default": "./dist/cjs/jestSetupFileAfterEnv.js" } }, + "dependencies": { + "@messageformat/core": "^3.0.1", + "globby": "^11.1.0" + }, "peerDependencies": { + "@ima/cli": ">=19.0.0", "@ima/core": ">=19.0.0", + "@ima/server": ">=19.0.0", "@ima/react-page-renderer": ">=19.0.0", "@testing-library/dom": ">=10.0.0", "@testing-library/jest-dom": ">=6.0.0", - "@testing-library/react": ">=12.0.0" + "@testing-library/react": ">=12.0.0", + "express": ">=4.0.0" }, "publishConfig": { "access": "public", diff --git a/packages/testing-library/src/rtl.tsx b/packages/testing-library/src/rtl.tsx index 4469d96022..77ebb153db 100644 --- a/packages/testing-library/src/rtl.tsx +++ b/packages/testing-library/src/rtl.tsx @@ -1,7 +1,7 @@ import { PageContext } from '@ima/react-page-renderer'; import { render } from '@testing-library/react'; import type { RenderOptions } from '@testing-library/react'; -import { ReactElement } from 'react'; +import type { ReactElement } from 'react'; // import of app/main is resolved by the jest moduleNameMapper import { ima, getInitialAppConfigFunctions } from 'app/main';