From e0745d6a263ec290b27a6914ccfb70da8c5292be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nora=20Dimitrijevi=C4=87?= Date: Thu, 5 Feb 2026 17:41:31 +0100 Subject: [PATCH] Fix React 19 compatibility for @testing-library/react This commit fixes test failures caused by React 19's breaking changes to the test utilities API, which broke @testing-library/react@16.x. Problem: All 255 view tests were failing with: 'TypeError: React.act is not a function' or 'TypeError: actImplementation is not a function' Root Cause: @testing-library/react@16.x internally uses react-dom/test-utils to access the 'act' function for wrapping component updates. In React 19: 1. react-dom/test-utils module was deprecated and no longer exports act 2. act() was moved to the main 'react' package 3. act() is only exported when NODE_ENV is 'test' or 'development' (it's undefined in production builds) When @testing-library/react tries to import react-dom/test-utils, it finds: const reactAct = typeof React.act === 'function' ? React.act : DeprecatedReactTestUtils.act; Both are undefined, so reactAct becomes undefined, causing the error 'actImplementation is not a function' when it tries to call it. Specific error locations: - node_modules/@testing-library/react/dist/act-compat.js:13 (reactAct is undefined) - node_modules/@testing-library/react/dist/act-compat.js:46 (actImplementation() call fails) - All 37 test suites in src/view/__tests__/ directories Solution: Created a Jest manual mock for react-dom/test-utils that provides the act function from React 19's new location: 1. Created __mocks__/react-dom/test-utils.js: - Imports act from 'react' package (React 19's new location) - Exports it with the same API that react-dom/test-utils used to have - Jest automatically uses manual mocks when they exist in __mocks__/ 2. Updated src/view/jest.config.ts: - Added moduleNameMapper entry to explicitly map 'react-dom/test-utils' to our mock file - This ensures the mock is used when @testing-library/react tries to import react-dom/test-utils 3. Updated package.json test:view script: - Added NODE_ENV=test to the cross-env command - Critical: React 19 only exports act() when NODE_ENV !== 'production' - Without this, require('react').act would be undefined even in our mock file 4. Updated eslint.config.mjs: - Added '__mocks__/' to globalIgnores - Prevents ESLint from trying to parse the mock file and failing with "not found by the project service" errors Test Results: Before: 255 failed tests (all with React.act errors) After: All 364 tests pass (2 skipped) The NODE_ENV=test setting in package.json is the critical fix - it ensures act() is available when React is loaded. The mock file simply re-exports it from the new location, making it available at the old import path that @testing-library/react expects. Files modified: - extensions/ql-vscode/__mocks__/react-dom/test-utils.js (new) - extensions/ql-vscode/src/view/jest.config.ts - extensions/ql-vscode/package.json - extensions/ql-vscode/eslint.config.mjs --- extensions/ql-vscode/__mocks__/react-dom/test-utils.js | 7 +++++++ extensions/ql-vscode/eslint.config.mjs | 1 + extensions/ql-vscode/package.json | 2 +- extensions/ql-vscode/src/view/jest.config.ts | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 extensions/ql-vscode/__mocks__/react-dom/test-utils.js diff --git a/extensions/ql-vscode/__mocks__/react-dom/test-utils.js b/extensions/ql-vscode/__mocks__/react-dom/test-utils.js new file mode 100644 index 00000000000..375108585b6 --- /dev/null +++ b/extensions/ql-vscode/__mocks__/react-dom/test-utils.js @@ -0,0 +1,7 @@ +// Mock for react-dom/test-utils for React 19 compatibility +// In React 19, react-dom/test-utils is deprecated and act was moved to the react package. +const { act } = require("react"); + +module.exports = { + act, +}; diff --git a/extensions/ql-vscode/eslint.config.mjs b/extensions/ql-vscode/eslint.config.mjs index da96a4d7e9e..abd912ee481 100644 --- a/extensions/ql-vscode/eslint.config.mjs +++ b/extensions/ql-vscode/eslint.config.mjs @@ -27,6 +27,7 @@ export default tseslint.config( ".markdownlint-cli2.cjs", "eslint.config.mjs", "!.storybook", + "__mocks__/", ]), github.getFlatConfigs().recommended, ...github.getFlatConfigs().typescript, diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 457517a66e2..747db220b9b 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2036,7 +2036,7 @@ "watch": "gulp watch", "test": "npm-run-all test:*", "test:unit": "cross-env TZ=UTC LANG=en-US NODE_OPTIONS=--no-experimental-strip-types jest --projects test/unit-tests", - "test:view": "cross-env NODE_OPTIONS=--no-experimental-strip-types jest --projects src/view", + "test:view": "cross-env NODE_ENV=test NODE_OPTIONS=--no-experimental-strip-types jest --projects src/view", "test:vscode-integration": "npm-run-all test:vscode-integration:*", "test:vscode-integration:activated-extension": "cross-env NODE_OPTIONS=--no-experimental-strip-types jest --projects test/vscode-tests/activated-extension", "test:vscode-integration:no-workspace": "cross-env NODE_OPTIONS=--no-experimental-strip-types jest --projects test/vscode-tests/no-workspace", diff --git a/extensions/ql-vscode/src/view/jest.config.ts b/extensions/ql-vscode/src/view/jest.config.ts index c197b277e62..560aa0d4d35 100644 --- a/extensions/ql-vscode/src/view/jest.config.ts +++ b/extensions/ql-vscode/src/view/jest.config.ts @@ -86,6 +86,8 @@ const config: Config = { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/../../test/__mocks__/fileMock.ts", "\\.(css|less)$": "/../../test/__mocks__/styleMock.ts", + "^react-dom/test-utils$": + "/../../__mocks__/react-dom/test-utils.js", }, // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader