Description
What kind of issue is this?
- React Compiler core (the JS output is incorrect, or your app works incorrectly after optimization)
- babel-plugin-react-compiler (build issue installing or using the Babel plugin)
- eslint-plugin-react-compiler (build issue installing or using the eslint plugin)
- react-compiler-healthcheck (build issue installing or using the healthcheck script)
Link to repro
https://github.com/ValentinGurkov/vitest-react-compiler-missing-coverage-repro
Repro steps
Describe the bug
Hello,
I've been testing out the new React compiler and have noticed that the code coverage report becomes incorrect with the compiler turned it. I believe it may be related to the way it changes the output react component code.
I've created a minimal reproduction repository to demonstrate the issue:
👉 https://github.com/ValentinGurkov/vitest-react-compiler-missing-coverage-repro, but I also want to share my findings here:
The button we are going to test is a simple one:
export const Button = () => {
return <button>Click me</button>;
};
As well as its test:
import { page } from '@vitest/browser/context';
import { Button } from '@repo/components/ui/button.js';
import { describe, expect, it} from 'vitest';
import { render } from 'vitest-browser-react';
describe(Button, () => {
it('renders with default variants', async () => {
render(<Button />);
const button = page.getByRole('button', { name: 'Click me' });
await expect.element(button).toBeInTheDocument();
});
});
The coverage report with the React compiler turned on looks like:
✓ browser (chromium) test/components/ui/button.test.tsx (1 test) 9ms
✓ Button > renders with default variants 9ms
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 20:34:46
Duration 632ms (transform 0ms, setup 133ms, collect 28ms, tests 9ms, environment 0ms, prepare 93ms)
% Coverage report from v8
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
button.tsx | 100 | 50 | 100 | 100 | 2
------------|---------|----------|---------|---------|-------------------
The component has no branching logic while the report shows are the are missing some. I believe I've managed to set up the vitest
configuration to also log the transformed component and we have:
import { jsxDEV } from "react/jsx-dev-runtime";
import { c as _c } from "react/compiler-runtime";
export const Button = () => {
const $ = _c(1);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = /* @__PURE__ */ jsxDEV("button", { children: "Click me" }, void 0, false, {
fileName: "/Users/<my-user>/Projects/vitest-react-compiler-coverage/src/components/ui/button.tsx",
lineNumber: 6,
columnNumber: 10
}, this);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
};
It looks like the React compiler introduces a conditional for memoization purposes. That may explain the coverage issue, though it raises the question: is it even possible to get 100% branch coverage for components compiled like this?
The test without the React compiler looks like:
- react({
- babel: {
- plugins: [
- ["babel-plugin-react-compiler", {}]
- ],
- },
- }),
+ react(),
✓ browser (chromium) test/components/ui/button.test.tsx (1 test) 10ms
✓ Button > renders with default variants 10ms
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 20:36:42
Duration 490ms (transform 0ms, setup 36ms, collect 6ms, tests 10ms, environment 0ms, prepare 92ms)
% Coverage report from v8
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
button.tsx | 100 | 100 | 100 | 100 |
------------|---------|----------|---------|---------|-------------------
The code coverage is 100% as expected. The output code also has no conditions:
import { jsxDEV } from "react/jsx-dev-runtime";
export const Button = () => {
return /* @__PURE__ */ jsxDEV("button", { children: "Click me" }, void 0, false, {
fileName: "/Users/<my-user>/Projects/vitest-react-compiler-coverage/src/components/ui/button.tsx",
lineNumber: 2,
columnNumber: 12
}, this);
};
Coming from vitest-dev/vitest#7843 (comment), it seems that babel's auxiliaryCommentBefore
also does not have any effect on this.
How do we see test coverage working once the React Compiler becomes standard?
How often does this bug happen?
Every time
What version of React are you using?
19.1.0
What version of React Compiler are you using?
19.1.0-rc.1