Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: Tests Fail Using Basic Configuration (React Native/TypeScript) #85

Open
therynamo opened this issue Mar 11, 2022 · 31 comments
Open

Comments

@therynamo
Copy link

therynamo commented Mar 11, 2022

I have come to @swc/jest because of the recent ts-jest issues kulshekhar/ts-jest#1967 (Granted it could be Jest it could be v8. Ultimately - I just wanted to move to SWC to get faster, more stable, and ultimately the most performant tests I can run).

I am trying to use this package with a React Native app - and am running into issues after I resolve the one listed here. I have tried a couple of different .swcrc configurations:

And just a basic one from the example in the repo here:

"@swc/jest",
{
"sourceMaps": true,
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}
]
},

The above works without a physical .swcrc file - which is what I'll opt for since our App wont be built using SWC yet (or maybe ever 🤷 Depends on the RN support in the future.)

The issue I'm running into now - is very similar:

 FAIL  MyApp/x/y/z.spec.tsx
  ● Test suite failed to run

    error: Expected ',', got ':'

       |
    51 |   applyWithGuard<TArgs: $ReadOnlyArray<mixed>, TOut>(
       |                       ^



    Caused by:
        0: failed to process js file
        1: Syntax Error

      at Compiler.transformSync (node_modules/@swc/core/index.js:137:25)
      at transformSync (node_modules/@swc/core/index.js:217:21)
      at Object.process (node_modules/@swc/jest/index.js:55:45)
      at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:612:31)
      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:758:40)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:815:19)

I haven't had any luck getting past this point and would LOVE some help troubleshooting how to move forward.

My current jest.config.js

module.exports = {
  preset: "react-native",
  transformIgnorePatterns: [
    "node_modules/(?!((jest-)?(@react-native|react-native)|react-clone-referenced-element|expo(nent)?|@expo(nent)?/.*|@react-navigation/.*|@sentry|@react-native-community/.*))"
  ],
  setupFilesAfterEnv: ["<rootDir>/someScriptThatWeRunThatMocksStuff.tsx"],
  cacheDirectory: ".jest/cache",
  clearMocks: true,
  testPathIgnorePatterns: [
    ".ts-temp",
    "<rootDir>/node_modules/",
    "<rootDir>/e2e/",
    "__fixtures__",
    "\\.{snap, png, svg}$"
  ],
  coveragePathIgnorePatterns: [
// patterns that we ignore coverage on
  ],
  modulePathIgnorePatterns: [".ts-temp"],
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
  // SWC
  testEnvironment: "node",
  transform: {
    "^.+\\.(t|j)sx?$": [
      "@swc/jest",
      {
        sourceMaps: true,

        jsc: {
          parser: {
            syntax: "typescript",
            tsx: true
          },

          transform: {
            react: {
              runtime: "automatic"
            }
          }
        }
      }
    ]
  }
};

tsconfig.json

{
  "compileOnSave": true,
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "lib": ["es6"],
    "moduleResolution": "node",
    "noEmit": true,
    "strict": true,
    "target": "esnext",
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "baseUrl": "./",
    "paths": {
      "/* comment */": "a bunch of paths for app aliasing"
    },
    "exclude": [
        "**/__mocks__/**",
        "node_modules",
        "**/node_modules/**",
        "babel.config.js",
        "metro.config.js",
        "jest.config.js",
        "e2e",
        "ios",
        "android",
        "assets",
        "coverage",
        "dist",
      ]
}

babel.config.js

module.exports = api => ({
  presets: [
    [
      "module:metro-react-native-babel-preset",
      { useTransformReactJSXExperimental: true }
    ]
  ],
  plugins: configurePlugins(api) // custom setup - base plugins listed below
});

// base plugins
const basePlugins = [
    [
      "@babel/plugin-transform-react-jsx",
      {
        runtime: "automatic"
      }
    ],
    "babel-plugin-transform-inline-environment-variables",
    [
      "module-resolver",
      {
        root: ["./"],
        extensions: [".ios.js", ".android.js", ".js", ".ts", ".tsx", ".json"],
        alias: { // aliases we use in the app - they match `paths` from the `tsconfig` },
      }
    ],
    [
      "formatjs",
      {
       // format js config
      }
    ],
    "react-native-reanimated/plugin",
}
@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

What's your input file?

@therynamo
Copy link
Author

What's your input file?

Sorry, I don't understand. I'm just running yarn jest. Are you asking what the test file itself looks like?

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

Yes. It's a parsing error so I need a repro case.

@therynamo
Copy link
Author

therynamo commented Mar 12, 2022

@kdy1 - I just put this together and was able to repro pretty quickly - https://github.com/therynamo/SWCJestRepro

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

@therynamo I checked it but it does not have a file named components/__tests__/EditScreenInfo.test.tsx

@therynamo
Copy link
Author

@kdy1 - so sorry about that 🤦 - I forgot to commit my changes 😅 . Try pulling from master again now.

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

It does not have the required file, namely MyApp/x/y/z.spec.tsx

 FAIL  MyApp/x/y/z.spec.tsx
  ● Test suite failed to run

    error: Expected ',', got ':'

       |
    51 |   applyWithGuard<TArgs: $ReadOnlyArray<mixed>, TOut>(
       |                       ^



    Caused by:
        0: failed to process js file
        1: Syntax Error

      at Compiler.transformSync (node_modules/@swc/core/index.js:137:25)
      at transformSync (node_modules/@swc/core/index.js:217:21)
      at Object.process (node_modules/@swc/jest/index.js:55:45)
      at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:612:31)
      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:758:40)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:815:19)


@therynamo
Copy link
Author

therynamo commented Mar 12, 2022

@kdy1 - yeah - that file was just an example file name. The edit screen info test in the repo I just linked causes the same error.

Were you able to pull down those updates I just pushed?

Here is a link to the new file https://github.com/therynamo/SWCJestRepro/blob/master/components/__tests__/EditScreenInfo.test.tsx

This test fails with the same error message.

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

applyWithGuard<TArgs: $ReadOnlyArray<mixed>, TOut>(

I need the file containing this line

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

Also, I don't pull files using my dev machines because it's a risk.

@therynamo
Copy link
Author

therynamo commented Mar 12, 2022

Yeah - that's the thing - there is no file containing that line. Which is the confusing part - the stack trace just indicates that there was an issue with @swc/core processing the file.

Also, I don't pull files using my dev machines because it's a risk.

It will likely help if you can run this code, I'm not sure how you'd be able to debug this without it.

This repo I linked above is one I just freshly created when you asked me for the input file- you can create the same exact repo on your own if that is more helpful:

https://reactnative.dev/docs/typescript

What I did to create this repo:

yarn global add expo-cli

expo init SWCJestRepro

# choose the option that has "tabs with typescript" (picture below)

yarn add -D babel-plugin-module-resolver @types/jest @testing-library/react-native @swc/core @swc/jest

Reference picture:
image

Then create a jest.config.js with the contents from above, linked in the issue description.
https://github.com/therynamo/SWCJestRepro/blob/431a25102e8a649da17d7a783a737f2c8b68583b/jest.config.js#L1-L42

Then add a babel.config.js file https://github.com/therynamo/SWCJestRepro/blob/431a25102e8a649da17d7a783a737f2c8b68583b/babel.config.js#L1-L19

Then create this test file with these contents:

https://github.com/therynamo/SWCJestRepro/blob/431a25102e8a649da17d7a783a737f2c8b68583b/components/__tests__/EditScreenInfo.test.tsx#L1-L10

(Note: if you don't fork the repo - consider deleting the .js test that is generated for the project by expo as it will just muddy the problem space and what we're trying to resolve here).


Alternatively - you can just fork the repo I made for us and for this issue. I understand that we have to be safe, especially when dealing with open source, but the repo I just created is about as bare bones as any React Native project can be. And I can't be sure that there isn't something going on with RN in this particular issue.

I'm very invested in finding a way to utilize @swc/jest for our project - so hopefully this helps you reproduce the issue that I have outlined above.

Let me know if you need any more information - happy to help where I can.

@therynamo
Copy link
Author

I'm out for the night, but I wonder if it's something in @testing-library/react-native. I'll check by writing a different test tomorrow.

Thanks again for looking into this!

@therynamo
Copy link
Author

Actually it looks related to the react native preset dosentmatter/babel-plugin-const-enum#3

@kdy1
Copy link
Member

kdy1 commented Mar 12, 2022

Oh, thanks! I'll extract the test case using github codespaces

@kdy1
Copy link
Member

kdy1 commented Mar 14, 2022

image

It seems like a bug of @react-native/polyfills

@therynamo
Copy link
Author

therynamo commented Mar 14, 2022

It seems like a bug of @react-native/polyfills

Hmm yeah - types should not be in a js file - good catch. I can try to look into that - and maybe put in a fix. Likely will just need to patch-package it if that removes all problems.

I'm investigating removing the react-native preset all together in the jest.config as it is mostly conditionally adding babel transforms - which SWC aims to eliminate in this situation. Right now I'm fighting with jest imports and getting everything to resolve correctly without Babel. I'll report back if there are any valuable findings.

EDIT: the latter effort was fruitless - ending in this error:

Can't resolve 'react/jsx-runtime' from `./components/EditScreenInfo'

@therynamo
Copy link
Author

Alright - so after I "patch" the node_modules/@react-native/polyfills/error-guard.js file to look like so:

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @flow strict
 * @polyfill
 */

let _inGuard = 0;


/**
 * This is the error handler that is called when we encounter an exception
 * when loading a module. This will report any errors encountered before
 * ExceptionsManager is configured.
 */
let _globalHandler = function onError(
  e,
  isFatal,
) {
  throw e;
};

/**
 * The particular require runtime that we are using looks for a global
 * `ErrorUtils` object and if it exists, then it requires modules with the
 * error handler specified via ErrorUtils.setGlobalHandler by calling the
 * require function with applyWithGuard. Since the require module is loaded
 * before any of the modules, this ErrorUtils must be defined (and the handler
 * set) globally before requiring anything.
 */
const ErrorUtils = {
  setGlobalHandler(fun) {
    _globalHandler = fun;
  },
  getGlobalHandler() {
    return _globalHandler;
  },
  reportError(error) {
    _globalHandler && _globalHandler(error, false);
  },
  reportFatalError(error) {
    // NOTE: This has an untyped call site in Metro.
    _globalHandler && _globalHandler(error, true);
  },
  applyWithGuard(
    fun,
    context,
    args,
    // Unused, but some code synced from www sets it to null.
    unused_onError,
    // Some callers pass a name here, which we ignore.
    unused_name,
  ){
    try {
      _inGuard++;
      /* $FlowFixMe[incompatible-call] : TODO T48204745 (1) apply(context,
       * null) is fine. (2) array -> rest array should work */
      /* $FlowFixMe[incompatible-type] : TODO T48204745 (1) apply(context,
       * null) is fine. (2) array -> rest array should work */
      return fun.apply(context, args);
    } catch (e) {
      ErrorUtils.reportError(e);
    } finally {
      _inGuard--;
    }
    return null;
  },
  applyWithGuardIfNeeded(
    fun,
    context,
    args,
  ) {
    if (ErrorUtils.inGuard()) {
      /* $FlowFixMe[incompatible-call] : TODO T48204745 (1) apply(context,
       * null) is fine. (2) array -> rest array should work */
      /* $FlowFixMe[incompatible-type] : TODO T48204745 (1) apply(context,
       * null) is fine. (2) array -> rest array should work */
      return fun.apply(context, args);
    } else {
      ErrorUtils.applyWithGuard(fun, context, args);
    }
    return null;
  },
  inGuard() {
    return !!_inGuard;
  },
  guard(
    fun,
    name,
    context,
  ) {
    // TODO: (moti) T48204753 Make sure this warning is never hit and remove it - types
    // should be sufficient.
    if (typeof fun !== 'function') {
      console.warn('A function must be passed to ErrorUtils.guard, got ', fun);
      return null;
    }
    const guardName = name ?? fun.name ?? '<generated guard>';
    function guarded(...args) {
      return ErrorUtils.applyWithGuard(
        fun,
        context ?? this,
        args,
        null,
        guardName,
      );
    }

    return guarded;
  },
};

global.ErrorUtils = ErrorUtils;

I now get this error from @swc/core:

 RUNS  components/__tests__/EditScreenInfo.test.tsx
thread '<unnamed>' panicked at 'internal error: entered unreachable code: invalid pattern: Expr(Member(MemberExpr { span: Span { lo: BytePos(982), hi: BytePos(999), ctxt: #0 }, obj: Ident(Ident { span: Span { lo: BytePos(982), hi: BytePos(988), ctxt: #0 }, sym: Atom('global' type=static), optional: false }), prop: Ident(Ident { span: Span { lo: BytePos(989), hi: BytePos(999), ctxt: #0 }, sym: Atom('ErrorUtils'  FAIL  components/__tests__/EditScreenInfo.test.tsxecma_parser/src/parser/expr.rs:1700:44
  ● Test suite failed to run

    failed to handle: internal error: entered unreachable code: invalid pattern: Expr(Member(MemberExpr { span: Span { lo: BytePos(982), hi: BytePos(999), ctxt: #0 }, obj: Ident(Ident { span: Span { lo: BytePos(982), hi: BytePos(988), ctxt: #0 }, sym: Atom('global' type=static), optional: false }), prop: Ident(Ident { span: Span { lo: BytePos(989), hi: BytePos(999), ctxt: #0 }, sym: Atom('ErrorUtils' type=dynamic), optional: false }) }))

      at Compiler.transformSync (node_modules/@swc/core/index.js:137:25)
      at transformSync (node_modules/@swc/core/index.js:217:21)
      at Object.process (node_modules/@swc/jest/index.js:55:45)
      at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:619:31)
      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:765:40)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:822:19)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.106 s
Ran all test suites.

You should be able to copy paste the fixed file without any types into whatever env you were using to reproduce this error.

@therynamo
Copy link
Author

Also - I've filed a bug with the RN team to see if this is intended or not facebook/react-native#33426

@coelhucas
Copy link

coelhucas commented Mar 25, 2022

Just a heads up that I'm having the same issue.

EDIT: The correct error (after setting up the config)

 ● Test suite failed to run


      × Expected ',', got ':'
        ╭────
     51 │ applyWithGuard<TArgs: $ReadOnlyArray<mixed>, TOut>(
        ·                     ─
        ╰────


    Caused by:
        0: failed to process input file
        1: Syntax Error

      at Compiler.transformSync (../../node_modules/@swc/core/index.js:137:25)
      at transformSync (../../node_modules/@swc/core/index.js:217:21)
      at Object.process (../../node_modules/@swc/jest/index.js:55:45)

@kdy1
Copy link
Member

kdy1 commented Mar 25, 2022

@coelhucas It's an issue of your config

@ncpa0cpl
Copy link

I see there's no good solution for this so I thought I should share what I did to work around this issue.

Since some of the file provided by the react-native libraries, for some ungodly reason have a .js extension but actually include TypeScript code, SWC is unable to process those files correctly, however babel can somehow make it work, so I've set jest transform patterns to use babel for any .js files from react-native libraries, while all other code is using SWC. Here's how jest transforms look like:

module.exports = {
  transform: {
    '^.+/((@)?react-native)/.+\\.(js|jsx)$': 'babel-jest',
    '^.+\\.(js|ts|jsx|tsx)$': '@swc/jest',
  }
}

I don't think this is a good solution, but it works and I've managed to achieve what I wanted (much faster test runs).

@Markos-Th09
Copy link

Markos-Th09 commented Aug 25, 2022

I see there's no good solution for this so I thought I should share what I did to work around this issue.

Since some of the file provided by the react-native libraries, for some ungodly reason have a .js extension but actually include TypeScript code, SWC is unable to process those files correctly, however babel can somehow make it work, so I've set jest transform patterns to use babel for any .js files from react-native libraries, while all other code is using SWC. Here's how jest transforms look like:

module.exports = {
  transform: {
    '^.+/((@)?react-native)/.+\\.(js|jsx)$': 'babel-jest',
    '^.+\\.(js|ts|jsx|tsx)$': '@swc/jest',
  }
}

I don't think this is a good solution, but it works and I've managed to achieve what I wanted (much faster test runs).

It isn't actually typescript but rather flow and this is why even if the typescript parser is used for everything it fails. btw thanks for the solution

@lucaslozz
Copy link

Hello guys, I'm facing the same issue. I add this and the same error ocrrur. Can some one help me?

module.exports = {
  transform: {
    '^.+/((@)?react-native)/.+\\.(js|jsx)$': 'babel-jest',
    '^.+\\.(js|ts|jsx|tsx)$': '@swc/jest',
  }
}

here is my repository https://github.com/lucaslozz/imhere.git

@camilossantos2809
Copy link

@lucaslozz Maybe it's transformIgnorePatterns, try leaving the array empty. In my case it worked.

@lucaslozz
Copy link

lucaslozz commented May 24, 2023

@camilossantos2809 I try it and nothing changed.is There someway to ignore those files?

@ncpa0cpl
Copy link

@lucaslozz can you show the exact error you are getting?

@lucaslozz
Copy link

@ncpa0cpl it's the same error of the issue

image

` Test suite failed to run

  × Expected ';', '}' or <eof>
    ╭─[D:\GIT\imhere\node_modules\@react-native\polyfills\error-guard.js:11:1]
 11 │
 12 │ let _inGuard = 0;
 13 │
 14 │ type ErrorHandler = (error: mixed, isFatal: boolean) => void;
    · ──┬─ ────────────
    ·   ╰── This is the expression part of an expression statement
 15 │ type Fn<Args, Return> = (...Args) => Return;
 16 │
 17 │ /**
    ╰────


Caused by:
    Syntax Error

  at Compiler.transformSync (node_modules/@swc/core/index.js:241:29)
  at transformSync (node_modules/@swc/core/index.js:348:21)   
  at Object.process (node_modules/@swc/jest/index.js:73:45)   
  at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:542:31)
  at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:671:40)
  at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:723:19)`

@ncpa0cpl
Copy link

So the file on which it fails for you (D:\GIT\imhere\node_modules\@react-native\polyfills\error-guard.js) is being transformed using swc instead of babel, that's why it fails. The regex I've given in my first post here should match this file import so I am unsure why is Jest using swc instead of babel for that file.

The only thing I can think of is that the actual file path is used to match against the regex rather than the import path, in which case the regex I've posted would not work on Windows, since Windows uses backslashes in filepaths and the regex was written for Unix systems that use regular slashes.

This is just a guess tho, and I am unable to verify that since I do not have access to any Windows machine. You can try replacing the slashes to backslashes in that regex and see if anything changes.

@lucaslozz
Copy link

@camilossantos2809 I've tried and now it's working. Thanks for help me!

@jzaefferer
Copy link

For me this extra transform helped:

transform: {
    '^.+/((@)?react-native)/.+\\.(js|jsx)$': 'babel-jest',
    '^.+\\.(t|j)sx?$': '@swc/jest',
  },

It then compiles react-native flow files with babel-jest, and my own files with swc. On a vanilla React Native project created with their template, the result is actually slightly slower.

Overall it looks like a better solution would be removing flow types before publishing the react-native npm packages: facebook/react-native#33426 (comment)

@ncpa0cpl
Copy link

@jzaefferer I wish you luck in convincing the facebook team to add a build step that will be necessary for this to RN project, I'd love to see them do that, it would help me a lot. In the project I am working on we have ~1.5k unit tests and it takes forever to complete, I will take anything that could speed it up by even a few percent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

8 participants