robertknight / babel-plugin-mockable-imports

Babel plugin that enables mocking of ES and CommonJS imports in tests
12 stars 1 forks source link

How to use this library within a basic typescript / nextjs / cypress boilerplate project #44

Closed glomotion closed 3 years ago

glomotion commented 3 years ago

Hello there! We were hoping to utilise a lib like this in order to evolve the basic sinon stubbing offered by default with cypress.

Im not having much luck getting this running within a stripped down starter nextjs ts repo tho. :(

Key Problems:

  1. typescript is not aware of the $imports exports. Is there a way to work around this?

    Screen Shot 2021-07-25 at 11 08 32 am
  2. getting run time errors when we try to run this babel plugin within our cypress install: (npm run test:cy:watch)

    Screen Shot 2021-07-25 at 11 10 24 am

Ive a basic repro of this up here: https://github.com/glomotion/stripped-down-next-rewire-ts

if any one could help shed some light on how to get this stuff working, we'd be super appreciative!

robertknight commented 3 years ago

typescript is not aware of the $imports exports. Is there a way to work around this?

One approach is to use a namespace import and then use of of TypeScript's various methods to bypass type checking when accessing a property on an object:

import * as someModule from '../some-module';
const $imports = (someModule as any).$imports;

There is a TypeScript example in the repo which uses this approach, with a helper:

https://github.com/robertknight/babel-plugin-mockable-imports/tree/master/examples/typescript

Ive a basic repro of this up here: https://github.com/glomotion/stripped-down-next-rewire-ts

I'm not familiar with Cyress or Next. The error message looks like it is referring to the statements that this code injects into a file to register symbols that can be mocked. If you look at the generated code for a module that has been processed by this plugin, it will contain code like:

import { originalName as localName } from '../other-module';
...
$imports = new ImportMap(...);
$imports.$add('localName', '../other-module', 'originalName', localName);

// References to `localName` will be changed to `$imports.localName` in the
// rest of the code, and this now returns either the original value of `localName`
// or a mock, depending on whether there is an active mock for this symbol or not.

It looks like there is some scenario where the $imports.$add line is not working as expected. If you look at the generated code for the module where the exception occurs, this might provide some insight. What I usually do to debug problems with generated code is to use the Babel CLI (from the @babel/cli package) to generate code using the same configuration that is used in the app and (babel <config options> path/to/source.js). Alternatively if there is a generated bundle of code you can look in that.

In your screenshots I see an export { $imports } from '..' line. You might encounter problems re-exporting the $imports object from one module in another. Within the module where this line exists there will be a conflict between the imported $imports and the local $imports and I don't know offhand what the outcome will be. Also the code that adds the $imports object will normally only be enabled in test builds, so it shouldn't be referenced in any code that might be run in a non-test environment.

glomotion commented 3 years ago

Thanks for the help @robertknight ill dig deeper.