4

Note: My goal it bring Rewire like functionally to the project. Be it using the Rewire package, babel-plugin-rewire or any other library that satisfies my goal. With that in mind, here is the detail:

I am trying to setup a new Typescript project with Mocha, and Chai. One of the unit tests require me to use rewire, which does not work with ES6 imports. So, I ended up using babel-plugin-rewire. But, I can't get it to work. For example, the following line:

    const jwtVerify = hello.__get__("hello");

Fails with TypeError: _get__(...).__get__ is not a function.

I have setup a minimalistic reproducible public repo here: https://github.com/naishe/typescript-babel if you want to play with it.

Here is the minimal project setup:

src/hello.ts

export default function(name: string) {
  return `Hello ${name}`;
}

function privateHello(name: string) {
  return `Not exported Hello ${name}`;
}

test/index.spec.ts

import hello from "../src/hello";
import { expect } from "chai";

describe("Typescript + Babel usage suite", () => {
  // This works!
  it("should return string correctly", () => {
    expect(hello("mocha")).to.be.equal("Hello mocha");
  });

  // These fail
  it("should check if jwtVerify function exists", () => {
    //@ts-ignore
    const jwtVerify = hello.__get__("hello");
    expect(jwtVerify).to.be.a("function");
  });

  it("should check if private function exists", () => {
    //@ts-ignore
    const privateHello = hello.__get__("privateHello");
    expect(privateHello).to.be.a("function");
  });

});

test/babel-register.js

const register = require('@babel/register').default;

register({ extensions: ['.ts', '.tsx', '.js', '.jsx'] });

babelrc.json

{
  "plugins": ["rewire"]
}

babel.config.js

module.exports = (api) => {
  // Cache configuration is a required option
  api.cache(false);

  const presets = [
    "@babel/preset-typescript",
    "@babel/preset-env"
  ];

  return { presets };
};

mocharc.json

{
  "extension": ["ts"],
  "spec": "test/**/*.spec.ts",
  "require": "test/babel-register.js"
}

Relevant part of package.json

"scripts": {
    "test": "mocha"
  },
  "devDependencies": {
    "@babel/cli": "^7.12.10",
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "@babel/preset-typescript": "^7.12.7",
    "@babel/register": "^7.12.10",
    "@types/chai": "^4.2.14",
    "@types/mocha": "^8.2.0",
    "@typescript-eslint/eslint-plugin": "^4.13.0",
    "@typescript-eslint/parser": "^4.13.0",
    "chai": "^4.2.0",
    "eslint": "^7.17.0",
    "mocha": "^8.2.1",
    "ts-node": "^9.1.1",
    "typescript": "^4.1.3"
  },
  "dependencies": {
    "babel-core": "^6.26.3",
    "babel-plugin-rewire": "^1.2.0"
  }

npm test emmits this:

  Typescript + Babel usage suite
    ✓ should return string correctly
    1) should check if jwtVerify function exists


  1 passing (4ms)
  1 failing

  1) Typescript + Babel usage suite
       should check if jwtVerify function exists:
     TypeError: _get__(...).__get__ is not a function
      at Context.<anonymous> (test/index.spec.ts:10:29)
      at processImmediate (internal/timers.js:456:21)

I have raised the concern on the babel-plugin-rewire, but it seems to be woefully silent. So, I am wondering if there is any other way to achieve this?

Nishant
  • 54,584
  • 13
  • 112
  • 127

1 Answers1

0

You need to just state the export name, as the function. Because you imported it as:

import hello from "../src/hello";

Just use:

const jwtVerify = hello;
expect(jwtVerify).to.be.a("function");

The reason is because you are trying to see if the object returned is a function (and you exported a function). So you just need to check the whole import name.


Additional side note:

If hello.ts is made like this:

export default function(name: string) {
  return `Hello ${name}`;
}

then to call the function you just:

console.log(hello('John'));

output: Hello John

because in the way you exported it, the import is the function.


Edit after question update:

I'm not familiar with Rewire, but there's a related question on Using Rewire with TypeScript that might be helpful.

If you need to have Babel with Typescript, try using the configurations shown in Microsoft - Typescript-Babel-Starter to begin with to ensure they both are configured to understand each other.

Here's also a nice I found that speaks some more about it Typescript and Babel Article.

Zach
  • 880
  • 5
  • 17
  • 1
    Thank you for your answer. I cannot test private members the way you have suggested. I wanted Rewire like functionality. That means, I would want to access, modify, and run tests on the functions that are not exported. I have updated my question, and it now includes a function that is not exported. Is there a way I can play with it in my tests? – Nishant Jan 19 '21 at 03:16
  • 1
    Hey Zach, I have gone through those articles before posting the question. In fact, I converted my project as mentioned in TypeScript Babel starter. I still did not get it working. I also tried using Jest which has pretty strong support for TypeScript and it seems neither Rewire nor the babel plugin worked. I do not want to compile down to ES5. I'd rather not use Rewire and rely on Typescript for private function validation, but the contributors would complain about lesser number of tests. :) – Nishant Jan 23 '21 at 05:46
  • 1
    Actually, you know what, I am going to attempt if I could tell tscofig to compile down to ES5 only when env is `test`, that might just do the job. – Nishant Jan 23 '21 at 05:50
  • Ah ok. :( Sorry I don’t have anything I could suggest. I guess as a last resort you can just leave the tests as JS? It’s not ideal, but if it comes down to it, it would work, and the rest of your project could still be TS. – Zach Jan 23 '21 at 09:16