I have some JavaScript modules which consume and declare globals. I'm aware that this is an anti-pattern, but it's a stopgap solution: until our dependencies come around to publishing their own ES module versions, we destructure them from globals. Here's what we do with Redux, as an example.
// Example: instead of
import createStore from '../path/to/redux.js';
// we do
const {createStore} = Redux;
This works a treat in the browser, and when the dependencies update it will be simple to refactor, however, testing in node with mocha is a different story:
// myReducer.test.mjs
import reducer from '../../src/redux-modules/myReducer';
import initialState from '../../src/redux-modules/myReducer';
export default function videosReducerTests() {
assert('reducer', function() {
it('should return initial state by default', function() {
reducer({}, {}).should.equal(initialState);
});
});
}
I've installed #std/esm to help node work with es modules, and set up my package.json with the following:
"devDependencies": {
"#std/esm": "^0.14.0",
},
"#std/esm":"js",
"scripts": {
"redux-test": "node -r #std/esm test/redux/index.mjs",
}
The result of running yarn redux-test is
ReferenceError: Redux is not defined
I've tried mocking the namespaces I need with global.Redux = {createStore: Function} etc. at the top of my test files, but those are all ignored, since imports are 'hoisted'.
Any tips on how to get around this block?
You might just stick with importing the full redux. It's small enough and may simplify browser/node things.
Related
Following is the entry point to my library, it generates a component with a dynamic tag:
// muvement.js
import { defineComponent, ref, onMounted, h } from 'vue';
const createMuvement = (tag) => {
return defineComponent({
name: `m-${tag}`,
setup(props, context) {
const root = ref(null);
onMounted(() => {
console.log(root.value);
});
return () => h(tag, { ...context.attrs, ref: root }, context.slots);
}
});
};
const muvement = (...tags) => {
const components = {};
tags.map((tag) => (components[`m-${tag}`] = createMuvement(tag)));
return components;
};
export { muvement };
It's expected to be consumed like so:
// Home.vue
<template>
<div>
<m-div>div</m-div>
<m-button>button</m-button>
</div>
</template>
<script>
import { muvement } from "muvement";
export default {
name: "Home",
components: {
...muvement("div", "button")
}
};
</script>
This works as expected when the library code is contained within the Vue app folder (assuming we are now importing from "#/components/muvement.js" instead of "movement").
That is:
-muvement-test-project (scaffolded with vue-cli)
- src
- views
- Home.vue
- components
- muvement.js
I've also published an alpha release that works fine when importing "muvement" after installing it directly from the npm registry (that is, npm install muvement instead of npm link muvement).
The Problem
During development, I want an app to test the library with that is separate from the library's directory.
I've used npm link to link the library to the test app (as I have done with many other projects in the past).
From /path/to/library
$ npm link
From /path/to/test/app
$ npm link muvement
So far so good. The module is available as a symlink in the test app's node_modules folder. So I import { muvement } from "muvement", run npm run serve, and... BOOM.
Everything explodes (see errors below). It's also probably worth noting that trying to import from the full path (i.e. C:/dev/npm/muvment/dist/es/index.js) results in the same issues as npm link does, so I don't think it has anything to do with the symlink directly.
This is what appears in the console:
For pretty much the entire day I have been trying to solve this one issue. I've seen several seemingly similar questions that were solved by settings Webpack's resolve.symlinks to false but that has no effect on my problem. I've read all through the docs and even Vue's source code (here is the offending line for those who are curious).
Since the warning suggests that the error is commonly attributed to async setup I thought maybe webpack was doing something weird that would make my code async. This doesn't seem to be the case as the call stack of both the working attempt and failed attempt are identical.
What's not identical is the scope.
Here is the scope for the example that is working:
And here is the failing one:
(Notice that the target parameter is null during the call to injectHook, which is obviously what prompts Vue to show a warning).
My question is, why does the location of the imported module make such a difference during the execution of the said module?
The library code and build setup are available here:
https://github.com/justintaddei/muvement
The test app is available here:
https://github.com/justintaddei/muvement/tree/example
If I've left out something important, please let me know in the comments. It's been a long day so I'm sure I've probably missed something.
Thank you.
The problem is your app is using two different vue dependencies under the hood - vue requires the same dependency to be used to keep track on reactivity, lifecycle, etc.
When you link a library npm/yarn will use that linked folder node_modules, but your app is using it's dependencies from it's node_modules.
When your app imports vue it will go app/node_modules/vue but when you import from your linked dependency it will be going to linked_dep/node_modules/vue.
app
node_modules
vue
linked library
node_modules
vue
One easy way to debug this issue is to change both vue dependency files with a console.log and check if the console is logging both.
I have been working on a React Typescript repo and have been running into an annoying issue where in jest is not able to resolve imports relative to root dir.
Cannot find module '~lib/dates' from 'utils.ts'
And this how the import looks like in the component / utils
import { abc } from '~lib/dates'; // this fails to run
If I change this to a relative path jest test runs works as expected
import { abc } from '../../lib/dates'; // this runs as expected
The same path work for some other directories and I am a bit stumped
import { xyz } from '~components/home/constants'; // jest resolves it
import { abc } from '~lib/dates'; // ERR
I tried including moduleNameWrapper in the jestConfig to see if it jest can resolve the imports correctly but it did not help.
package.json
"jest": {
...
"moduleNameWrapper": {
"^~(.*)$": "<rootDir>/src/$1"
}
}
I could for sure update the VS code setting so that auto imports are resolved relatively to the file and not with the root dir but this has been bugging me for a while. It would be great if anyone has any pointers on how best to resolve this.
I am on a monorepo with the following directory structure
repo
server
client
src
components
lib
utils
package.json
Your implementation looks right. But it looks like the option moduleNameWrapper was the wrong option, it's supposed to be moduleNameMapper.
I also have an example as same as you which also uses babel as transplier, it works fine as I added moduleNameMapper. Here is the my example:
Jest configuration:
https://github.com/tmhao2005/lerna-demo/blob/master/packages/share/jest.config.js
Here is the file for testing:
https://github.com/tmhao2005/lerna-demo/blob/master/packages/helper/src/index.ts
https://github.com/tmhao2005/lerna-demo/blob/master/packages/helper/src/index.test.ts
forget the ~ character;
first define root directory to jest (i.e. src/);
then import your stuff from that root directory; (e.g. import { abc } from 'lib/dates')
by the way you can always import your stuff from default root without any configuration like this: import { abc } from 'src/lib/dates'
further read if you are using create-react-app jest absolute import
i am currently setting up a test environment and come across a problem, that all my calls to .hasClass return false.
Current Setup: My react components import scss files with an import statement. For example:
import styles from "./text.scss";
To test the components i had to define the moduleNameMapper in the jest.config.js file like so:
moduleNameMapper: { "\\.(scss|less)$": "<rootDir>/__mocks__/styleMock.js" },
I think that the moduleNameMapper is kind of responsible for the problems, since it replaces via default all scss definitions with an empty module. (styleMock.js content is just module.exports = {};)
But i need it to test my components, otherwise it would result in an error, when jest tries to load the scss imports.
When i now try this:
it("is Title", () => {
const wrapper = shallow(<Text textType={TextType.Title} />);
expect(wrapper.find("div").hasClass("Title")).toEqual(true);
});
It always returns false.
Is there any solultion on how to test the scss classes (with .hasClass from enzyme?), when you have scss import statements in your component?
Found a solution finally that works!
For me i had to install the identity-obj-proxy via
npm install --save-dev identity-obj-proxy
and then add it to the jest config file like that:
moduleNameMapper: {
"^.+\\.(css|less|scss)$": "identity-obj-proxy"
}
After that my class name's are now in the snapshots correctly and no more undefined classnames!
In addition to that i can now finally use the .hasClass feature of enzyme and check if new css class had been added to a div and so on. (Finally i can go into testing those conditional rendering parts!)
I am trying to manually include the #material/drawer npm package into my Ember app. I tried following this guide but I'm running into some weird errors in my Chrome dev console:
Uncaught SyntaxError: Unexpected token *
Uncaught ReferenceError: define is not defined
The first is from the imported node_modules/#material/drawer/index.js file and the second is from my generated shim.
My component code:
import Component from '#ember/component';
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
export default Component.extend({
init() {
this._super(...arguments);
const drawer = new MDCTemporaryDrawer(document.querySelector('.mdc-drawer--temporary'));
document.querySelector('.menu').addEventListener('click', () => drawer.open = true);
}
});
In my ember-cli-build.js:
app.import('node_modules/#material/drawer/index.js');
app.import('vendor/shims/#material/drawer.js');
My generated shim:
(function() {
function vendorModule() {
'use strict';
return {
'default': self['#material/drawer'],
__esModule: true,
};
}
define('#material/drawer', [], vendorModule);
})();
What exactly am I doing wrong? It almost seems as though raw ES6 code got imported rather than compiled into my JS build output.
I also read this SO post but there are too many answers and I'm not sure which to do. It seems this specific answer is what I'm trying to do but not verbatim enough.
Creating a shim only ensures that ember-cli gets an AMD module, which you then can import in your app files.
If the npm package needs a build or transpiling step beforhand, this won't work.
You need a way to get the package build within the ember-cli build pipeline.
Luckily there are addons which can take care of this for you: ember-auto-import and ember-cli-cjs-transform.
You may have also heard of ember-browserify, which does the same thing, but it's deprectaed in favor of ember-auto-import.
I'd suggest you try ember-auto-import:
ember install ember-auto-import
You then should be able to import as you tried:
import { MDCTemporaryDrawer, MDCTemporaryDrawerFoundation, util } from '#material/drawer';
No shim or app.import needed, as ember-auto-import will take care of this for you.
I'm having a great deal of trouble using Enzyme and Mocha to test my React project. I have a test like this:
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import { ChipInput} from '../client/components/Chips';
describe('<ChipInput />', _ => {
it('rocks', done => {
done();
});
});
And when ChipInput gets imported, that file imports something with an absolute path, e.g. /lib/collections/tags, and then Mocha errors out because it apparently only does relative paths. How do I get this working?
EDIT:
The actual error:
Error: Cannot find module '/lib/collections/tags'
This happens because /tests/ChipInput-test.js imports the ChipInput component from /client/components/Chips/index.js, which has the following lines:
import React from 'react';
import {
MapsLocalOffer as TagIcon,
ImageRemoveRedEye as InsightIcon,
EditorInsertChart as TestIcon,
SocialPerson as UserIcon,
} from 'material-ui/svg-icons';
import { Tag } from '/lib/collections/tags'; // error thrown here
import { Insight } from '/lib/collections/insights';
// import { Test } from '/lib/collections/tests';
import Chip from './Chip';
import ChipDisplay from './ChipDisplay';
import ChipInput from './ChipInput';
import * as chipTypes from './chip-types';
To anyone hitting here from google, while ffxsam's answer will work, there are many ways to accomplish this. Node's require allows for a base to be set either via environment variable or programmatically, allowing for simple absolute paths that don't require the leading slash (require("my/module"); vs require("/my/module");).
I use gulp as a taskrunner, so my preferred technique is to use app-module-path to do the following at the top of my gulpfile (this will work anywhere, so long as you haven't encountered any absolute requires yet):
require('babel-core/register'); //for mocha to use es6
require('app-module-path').addPath(__dirname + '/src'); //set root path
//I also use webpack to pull in other extensions, so I
//want mocha to noop out on them
require.extensions['.css'] = _.noop;
require.extensions['.scss'] = _.noop;
require.extensions['.png'] = _.noop;
require.extensions['.jpg'] = _.noop;
require.extensions['.jpeg'] = _.noop;
require.extensions['.gif'] = _.noop;
For a more complete rundown, check out this gist by github user branneman: https://gist.github.com/branneman/8048520
This has been awhile, but I want to share my solution here, just in case, all solutions above don't work in your specific situation. I was looking for a solution to fix our unit test, which failed "Error: Cannot find module 'components/shared/xyz', our 'components' folder is under 'client/src' folder, so I came up with the following solution which works for us.
npm install babel-plugin-module-resolver --save-dev
{
'plugins': [
'babel-plugin-module-resolver',
{ 'root': ['client/src'] }
]
}
Here's the solution, nice and simple!
https://github.com/mantrajs/babel-root-slash-import
Basically, install said package:
npm install babel-root-slash-import --save-dev
Add the plugin to .babelrc:
{
"plugins": [
"babel-root-slash-import"
]
}
And it's good to go.