kulshekhar / ts-jest

A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.
https://kulshekhar.github.io/ts-jest
MIT License
6.93k stars 453 forks source link

Line numbers aren't mapped correctly in TSX files when using Node 8 AND React 16 #334

Closed jimthedev closed 6 years ago

jimthedev commented 6 years ago

When using Node version 8 tsx-errors.spec.ts fails with:

Summary of all failing tests
 FAIL  tests/__tests__/tsx-errors.spec.ts
  ● TSX Errors › should show the correct error locations in the typescript files

    expect(string).toContain(value)

    Expected string:
      "FAIL __tests__/Button.test.tsx
      ✓ Button renders correctly (13ms)
      ✕ BadButton should throw an error on line 18 (6ms)

      ● BadButton should throw an error on line 18

        Error in Bad button

          at BadButton.Object.<anonymous>.BadButton.render (Button.tsx:34:15)
          at Object.<anonymous> (__tests__/Button.test.tsx:11:14)
              at Promise (<anonymous>)
              at <anonymous>
          at process._tickCallback (internal/process/next_tick.js:188:7)

    Test Suites: 1 failed, 1 total
    Tests:       1 failed, 1 passed, 2 total
    Snapshots:   1 passed, 1 total
    Time:        2.75s
    Ran all test suites.
    "
    To contain value:
      "Button.tsx:18:17"

      at Object.<anonymous> (tests/__tests__/tsx-errors.spec.ts:10:20)
          at Promise (<anonymous>)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

Test Suites: 1 failed, 22 passed, 23 total
Tests:       1 failed, 67 passed, 68 total
Snapshots:   2 passed, 2 total
Time:        23.794s
Ran all test suites matching /^(?!(.*watch.spec.ts$)).*/i.
npm ERR! Test failed.  See above for more details.

I expect the test to pass.

This test is in the ts-jest repo so reproducing just involves upgrading to node 8 via nvm, n or some other method.

kulshekhar commented 6 years ago

Thanks for reporting this. I was able to reproduce this issue. It looks related to react 16 in some way.

jimthedev commented 6 years ago

Yes I think you're correct. I would be happy to help on this if you point me in the right direction of what in React 16 might have changed the error line mappings?

kulshekhar commented 6 years ago

I've pushed an additional change to your PR which should, hopefully, result in a failed build. It'll be good to have a common point of reference when debugging this issue.

I would be happy to help on this if you point me in the right direction of what in React 16 might have changed the error line mappings?

The issue appears to arise due to some changes in react-test-renderer

jimthedev commented 6 years ago

Sounds good!

jimthedev commented 6 years ago

Yeah react-test-renderer is where I got to and then figured I was out of my element since I wasn't sure what it did.

kulshekhar commented 6 years ago

This is probably being caused due to the new error handling mechanism that React has. I'm not sure how this can be fixed in ts-jest.

jimthedev commented 6 years ago

I wonder if we can add in a custom log output in componentDidCatch that captures the right line.

kulshekhar commented 6 years ago

componentDidCatch doesn't contain any line numbers

jimthedev commented 6 years ago

:( Dang.

kulshekhar commented 6 years ago

they have some sort of a plugin for those using Babel but it won't help here https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html#component-stack-traces

GeeWee commented 6 years ago

If this is due to the react code - can we just throw our errors in non-react code?

gaearon commented 6 years ago

We just read <div />._source.lineNumber. I don’t know where it comes from for TypeScript users. If you use Babel, babel-plugin-transform-react-jsx-source generates it as part of JSX.

So to fix it, you would need to either remove babel-plugin-transform-react-jsx-source (assuming you somehow use it after running TS), or add source metainformation at TypeScript level.

kulshekhar commented 6 years ago

@GeeWee

If this is due to the react code - can we just throw our errors in non-react code?

I'm not sure I understand. Could you please elaborate?

@gaearon

We just read <div />._source.lineNumber

that's set by the babel-plugin-transform-react-jsx-source plugin.

I don’t know where it comes from for TypeScript users.

It isn't available at the moment the TypeScript file is processed. It wasn't required prior to v16 as all errors in .tsx files were displayed with the correct line numbers.

So to fix it, you would need to either remove babel-plugin-transform-react-jsx-source (assuming you somehow use it after running TS)

We don't currently use it but I actually think otherwise. We might need to use it if we can't find any solution. In that case as well, the plugin should be capable of using sourcemaps (that are emitted after we process .tsx files) to display the correct line numbers. Does the plugin handle sourcemaps?

or add source metainformation at TypeScript level

In the worst case, this might be the way to go.

kulshekhar commented 6 years ago

Curiously, it only fails in node version 8. I was able to reproduce this as part of #333

GeeWee commented 6 years ago

I'm not sure I understood it right the first time around. Do we still think this is due to react error boundaries?

It only failing in node 8 says no to me, but it's very curious.

gaearon commented 6 years ago

It wasn't required prior to v16 as all errors in .tsx files were displayed with the correct line numbers.

React 16 didn't change anything in how line numbers are associated with JSX. There are only two things that changed:

React can't possibly affect the line numbers from JavaScript stack. Those are reported by the JavaScript engine. I now see that your report was about JS line numbers so my previous comments about JSX transform were not relevant.

kulshekhar commented 6 years ago

I'm going to try and find some time to dive deeper into this but in case someone else is looking at it, here's what the situation looks like:

kulshekhar commented 6 years ago

Some debugging threw up this error message:

    Error
        console.error ../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2055
          The above error occurred in the <BadButton> component:
              in BadButton
              in BadButtonBoundary

          React will try to recreate this component tree from scratch using the error boundary you provided, BadButtonBoundary.

        console.error ../../node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2055
          The above error occurred in the <BadButtonBoundary> component:
              in BadButtonBoundary

I have a suspicion that some code in react is throwing the built-up error and in the process (directly or indirectly) suppressing the stack trace

kulshekhar commented 6 years ago

@gaearon

React 16 didn't change anything in how line numbers are associated with JSX. There are only two things that changed:

  • React now catches errors during rendering before rethrowing them.
  • React adds extra information in the form of component stack when JSX line numbers are available.

React can't possibly affect the line numbers from JavaScript stack. Those are reported by the JavaScript engine. I now see that your report was about JS line numbers so my previous comments about JSX transform were not relevant.

Thanks for the insights.

The absence of a stack trace is the likely cause of this issue.

Is it possible that some edge case handler (or some branch of code) in the renderer is suppressing the stack trace? The reason I think this might be the case is because this fails only on Node 8 and not on the earlier versions.

gaearon commented 6 years ago

Where do you see the stack trace being suppressed?

kulshekhar commented 6 years ago

Sorry about that!

That was the result of some changes that I had made while debugging. Please disregard my previous comment (updated)

kulshekhar commented 6 years ago

I've spent some time on this and don't think this is an issue with ts-jest. ts-jest is processing the tsx files correctly and is adding the sourcemap as well as the code to use source-map-support.

There's not much we can do about this here, I'm afraid.

The readme needs to be updated with a note about react 16 + node 8.

fbartho commented 6 years ago

I noticed that I get correct source-mapping in thrown Errors when I set "useBabelrc": false, in the package.json["jest"]["globals"]["ts-jest"] config block (alongside "mapCoverage": true)

(To be clear, this doesn't mean I got correct code-coverage, just that errors thrown in TS files needed ts-jst to avoid babel).

Maybe this helps?

jamietre commented 6 years ago

I was wondering if there is any more information or progress on this? I'm trying to set up Jest for the first time and stack trace line numbers are incorrect as per this issue. It's a little different for me:

1) We are on React 15 2) Node version has no effect (tried 6, 7, 8, 9) 3) Source maps work perfectly when I debug in VS Code, so it seems unrelated to sourcemaps themselves.

I've sought support via Discord and there are unanswered Stack Overflow questions with the same issue, there doesn't seems to be any resolution or workaround. This seems like a pretty big problem.

Does anyone more familiar with Jest & this issue know if this is affecting everyone using Jest + TypeScript or just isolated cases? It seems odd that it's been going on so long unresolved if it's affecting a large number of people. I'm not sure how I can make a case that we should be adopting Jest if I can't get source maps working correctly, but i've been working on this for several days and nothing seems to work so I'm pretty baffled. I've tried various workarounds from older issues, e.g. turn on mapCoverage, disableSourceMapSupport option, nothing makes any difference.

singuerinc commented 6 years ago

I fixed it by adding the --mapCoverage flag

react-scripts test --env=jsdom --coverage --mapCoverage
jamietre commented 6 years ago

Tried adding mapCoverage, it's been deprecated as of 22.4.0. I've tried literally every possible combination of options - useBabelrc, disableSourceMapSupport, in babelrc "sourceMap": "inline", nothing makes any difference.

6220119 commented 6 years ago

Not sure what magic it is. We also have this problem. However, running the tests with --mapCoverage flag fixed this. versions: ts-jest: 22.4.5 node: v9.10.1 react: v16.0.0

maoueh commented 6 years ago

@jamietre Did you try the CLI flag --mapCoverage or the config option? I did both test and strangely, only the command line flag --mapCoverage works. Using mapCoverage: true is not working.

That's really strange :(

fbartho commented 6 years ago

@maoueh ! -- that's a big catch. I was only using the config option. I haven't tested the CLI flag. I don't know when I'll get to experiment with implementing that in our repo, but that's really key.

SimenB commented 6 years ago

For people coming to this issue from the FAQ, mapCoverage is removed in later versions of Jest as it's not needed anymore.

(that won't help create-react-app I suppose, as that's on an older version of jest)

huafu commented 6 years ago

@jamietre @6220119 @maoueh @fbartho you can try the beta release of ts-jest (see #697). It should fix your coverage/line-mapping issues.

@SimenB I'd love to chat about jest+ts-jest. We now have our slack channel so let's chat there if you can.

SimenB commented 6 years ago

@huafu sorry, missed your message. I'm already a member of way too many Slacks, but you can join us over on Reactiflux? https://discord.gg/MWRhKCj

fider-apptimia commented 4 years ago

Same problem for non-react projects - invalid stack trace line number.

Node v12.16.1 ts-jest@25.2.1 jest@25.1.0

(it is showing .ts file but line number isn't mapped to original ts file)

adambeben commented 4 years ago

@fider-apptimia I had the same problem in non-react project when I was using async/await. Changing target in tsconfig.json to ES2019 fixed it for me.

This is how my tsconfig.json looks like (only target is edited, the rest are default tsc --init values):

{
  "compilerOptions": {
    "target": "ES2019",                       /* ES2019 is rquired to display proper stack trace when using async/await and '?.' operator */
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}
fider-apptimia commented 4 years ago

@adambbn here is my full tsconfig:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "noImplicitAny": true,
    "module": "CommonJS",
    "moduleResolution": "node",
    "allowJs": true,
    "strict": true,
    "isolatedModules": true,
    "pretty": true,
    "sourceMap": true,
    "target": "ES2019",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "rootDir": "./",
    "outDir": "./dist"
  },
  "exclude": ["dist", "node_modules"]
}

Still invalid stack trace with ES2019 - stack traces of errors throwed from my .ts file wrapped by jest are still not mapped.

Cellule commented 1 year ago

Just in case someone stumbles on this issue in the future. I've hit that issue again recently. While I was trying to make a minimal repro, I was struggling to do it in a controlled environment. After much reducing in my main project I finally identified the culprit. There was a ts-node import and register which caused bad line numbers

I managed to make a minimal repro here https://github.com/Cellule/ts-jest-line-numbers

I doubt this is something that ts-jest should handle/fix ? But if that can help another than I'm glad