I have a few circular dependencies in my application. It doesn't affect in production, the app works OK. But now I started to write unit tests using jest and now some of my imports returns undefined. And because of that I can't write a single test.
Even when I try to render whole application, there are a few undefined in the imports.
I can't remove these circular dependencies, because it would take a lot of time.
How can I deal with it?
Here are some examples of errors given by Jest
TypeError: Cannot read properties of undefined (reading 'sensorTypeRenderer')
4 | import { Renderers, ColumnDescriptionGenerators } from '#components';
> 6 | import sensorTypeRenderer = Renderers.sensorTypeRenderer;
Here's the service is not found because EventsGroupsStore is undefined
ServiceNotFoundError: Service with "<UNKNOWN_IDENTIFIER>" identifier was not found...
> 204 | return Container.get(EventsGroupsStore);
The problem was not really with the circular dependencies. I just configured aliases incorrectly.
That's how it should be:
moduleNameMapper: {
"^#src(.*)": "<rootDir>/src$1",
"^#tests(.*)": "<rootDir>/tests$1",
"^#data(.*)": "<rootDir>/tests/mockData$1",
"^#domain(.*)": "<rootDir>/src/domain$1",
"^#service(.*)": "<rootDir>/src/service$1",
"^#utils(.*)": "<rootDir>/src/utils$1",
"^#view(.*)": "<rootDir>/src/view$1",
"^.+\\.(css|scss)$": "<rootDir>/tests/styleMock.js",
"^#resources": "<rootDir>/tests/styleMock.js",
"^#components(.*)": "<rootDir>/src/components$1",
},
And I just didn't add these (.*) and $1
Related
TL;DR
How to translate a node script like this:
"test": "NODE_ENV=test riteway -r #babel/register 'src/**/*.test.js' | tap-nirvana",
to use SWC instead of Babel?
Context
We recently upgraded our Next.js version. Next.js now supports SWC instead of Babel.
The unit tests for React in our project are written with RITEway. The test command is:
"test": "NODE_ENV=test riteway -r #babel/register 'src/**/*.test.js' | tap-nirvana",
It transforms the files with Babel first because otherwise import statements and JSX would cause errors.
During our attempts to migrating to SWC, we had no luck with the CLI. However, we found the #swc-node/register package. It allowed us to transform our command like this:
"test": "riteway -r #swc-node/register src/**/*.test.js | tap-nirvana"
which fixes new syntax like the import statement and a test like this would run:
import { describe } from 'riteway';
describe('my test', async assert => {
assert({
given: 'true',
should: 'be true',
actual: true,
expected: true,
});
});
However, tests for React components like this
import { describe } from 'riteway';
import render from 'riteway/render-component';
import Authentication from './user-authentication-component';
describe('user authentication component', async assert => {
const $ = render(<Authentication />);
assert({
given: 'just rendering',
should: "render 'Authentication'",
actual: $('*:contains("Authentication")').text(),
expected: 'Authentication',
});
});
still throw the following error:
$ yarn test
yarn run v1.22.17
$ riteway -r #swc-node/register src/**/*.test.js | tap-nirvana
/Users/user/dev/learning-flow/node_modules/#swc/core/index.js:135
return bindings.transformSync(isModule ? JSON.stringify(src) : src, isModule, toBuffer(newOptions));
^
Error: error: Expression expected
|
7 | const $ = render(<Authentication />);
| ^
error: Expression expected
|
7 | const $ = render(<Authentication />);
| ^
error: Unexpected token `)`. Expected this, import, async, function, [ for array literal, { for object literal, # for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier
|
7 | const $ = render(<Authentication />);
| ^
Caused by:
0: failed to process js file
1: Syntax Error
at Compiler.transformSync
How can we create that command so that it runs with SWC?
After some experimentation I found out that it works if you import React from 'react'; in every file (both the component as well as the test) and change the file extensions to .jsx as described in the docs.
However, this is unsatisfying, as we'd like to use React 17's JSX transform feature to avoid having to import React in every file. Additionally, we'd like to keep all file endings .js.
We tried setting .swcrc without any luck so far.
I'm posting this answer here in case no way to configure .swcrc can be found.
I'll assume your question is only about jest and not about the webpack setup for swc.
I've never used swc myself in jest so this is just a shot in the dark, but I found a package for jest called #swc-node/jest which allows you to use a transformer like:
module.exports = {
transform: {
'^.+\\.(t|j)sx?$': ['#swc-node/jest'],
},
}
Which should allow you to transpile all [jt]sx? imports.
There's also a different one called #swc/jest which seems to do the same thing.
I'd start with trying those.
Your issue is that jest isn't able to parse your code, for example if you were using typescript you'd need something like ts-jest in order to parse your TS for you, I think in this case you need a swc/jest transpiler which should first compile your code and output it to memory, then funnel it to jest to run it.
The alternative is that you could compile the tests before hand using swc, then run jest on the compiled files as a separate step. At least with TS you can do that, first compile everything using tsc and then run jest on the .js output. I'm sure swc should work the same.
As to why the #swc-node/register package you're using isn't working, I have no idea.
I recently built a library with Rollup that has a few non-usual bits. That includes for instance, loading up a wasm module, workers with importScripts and a few occurences of eval() in the global scope.
Now I used the rollup-starter-app to create a demonstrator and client app for that library. The repo is https://github.com/frantic0/sema-engine-rollup
I managed to get everything working, after hitting a few walls and adding the following rollup plugins
import { wasm } from "#rollup/plugin-wasm";
import workerLoader from "rollup-plugin-web-worker-loader";
import dynamicImportVars from "#rollup/plugin-dynamic-import-vars";
import copy from "rollup-plugin-copy";
However, in the build output, I'm getting this massive log of what seems to be some encoding...
I'm not sure where this log is coming from and it is so massive that it clears out all the information of the build in the terminal...
What is the best way to tackle this issue and how to debug it effectively?
based on the suggestion #lukastaegert on the rollup issues, one solution is to redirect stderr into a file to read the log.
To do that you can add the following to your rollup command
"rollup -cw 2>err.log 1>out.log"
this allows to further inspect the build log but doesn't solve the error
[EDIT]
After a bit of peeking around Rollup's github issues and source, I found the warning categories and how to deactivate warnings.
Basically, we need to add a function onwarn to rollup.config.js. The first code section below shows the function. The second one show where we should add it on the rollup.config.js
const onwarn = (warning) => {
// Silence warning
if (
warning.code === 'CIRCULAR_DEPENDENCY' ||
warning.code === 'EVAL'
) {
return
}
console.warn(`(!) ${warning.message}`)
}
export default {= {
inlineDynamicImports: !dynamicImports,
preserveEntrySignatures: false,
onwarn,
input: `src/main.js`,
output: {
I am trying out Stitch, a serverless/hosted JavaScript environment from MongoDB. My main purpose is to help me learn modern JavaScript, but I am trying to write a useful app as well.
I have written the following function, and saved it in my Stitch app. I believe this follows the documented way to write functions in Stitch, and I have tested it from the Stitch administration console:
exports = function(query){
const http = context.services.get("HTTP");
const urlBase = context.values.get("stackOverflowApiUrl");
const options = [
'order=desc',
'sort=activity',
'site=stackoverflow',
'q=' + encodeURIComponent(query),
'user=472495',
'filter=!--uPQ.wqQ0zW'
];
return http
.get({ url: urlBase + '?' + options.join('&') })
.then(response => {
// The response body is encoded as raw BSON.Binary. Parse it to JSON.
const ejson_body = EJSON.parse(response.body.text());
return ejson_body.total;
});
};
This code is pretty simple - it obtains an http object for making external API fetches, and obtains a configuration value for a URL urlBase to contact (resolving to https://api.stackexchange.com/2.2/search/excerpts) and then makes a call to the Stack Overflow Data API. This runs a search query against my user and returns the number of results.
So far so good. Now, I want to call this function locally, in Jest. To do this, I have installed Node and Jest in a local Docker container, and have written the following test function:
const callApi = require('./source');
test('Simple fetch with no user', () => {
expect(callApi('hello')).toBe(123);
});
This fails, with the following error:
~ # jest
FAIL functions/callApi/source.test.js
✕ Simple fetch with no user (3ms)
● Simple fetch with no user
TypeError: callApi is not a function
2 |
3 | test('Simple fetch with no user', () => {
> 4 | expect(callApi('hello')).toBe(123);
| ^
5 | });
6 |
at Object.<anonymous>.test (functions/callApi/source.test.js:4:12)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.418s
Ran all test suites.
(In fact I was expecting it to fail, since it contains a global object context that Jest does not have access to. I will work out how to mock that later, but for now Jest cannot even see the function at all).
I suspect I can see the reason - in the Jest introduction docs, one has to do this for the SUT:
module.exports = function() { ... }
However the Stitch docs seem to require functions to be defined as:
exports = function() { ... }
I do not have a background in JavaScript to understand the difference. I could try module.exports in Stitch, but I would rather not, since this would either not work now, or cause a breakage in the future. Can Jest be instructed to "see" bare exports without the module prefix?
Incidentally, I have picked Jest because it is popular, and because some of my JavaScript colleagues vouch for it. However, I am not wedded to it, and would be happy to use something else if it is known to be better for Stitch development.
Update
Following the useful answer from jperl below, I find that the following construction is not possible in Stitch:
module.exports = exports = function() {}
I also cannot do this:
exports = function() {}
module.exports = exports
If I try either, I get the following error:
runtime error during function validation
So it looks like I have to get Jest to work without module.exports, or create a glue file that imports the exports version into module.exports, with the main file being used by Stitch, and the glue importer being used by Jest.
I suggest you to read this thread. And you're right in thinking it has to do with modules.exports vs exports. The thing is that module.exports and exports first point to the same thing. So something like this works:
//modify the same object that modules.exports is pointing to
exports.a = {}
exports.b = {}
but this won't:
exports = {}
Why? Because now exports points to something else than module.exports so what you're doing has no effect at all.
Update
Following some updates in the comments, we came to the view that Stitch does not seem to support the export format that Jest requires.
This is an addendum to jperl's answer, to show how I got Jest working while respecting Stitch's limitations.
Firstly, it is worth noting how a Stitch application is laid out. This is determined by the import/export format.
auth_providers/
functions/
function_name_1/
config.json
source.js
function_name_2/
config.json
source.js
...
services/
values/
The config.json file is created by Stitch remotely, and is obtained through a export. This contains ID information to uniquely identify the function in the same folder.
I believe it is common JavaScript practice to mix tests with source code, so I am following that style (I am new to modern JS, and I confess I find this style untidy, but I am running with it nevertheless). Thus I add a source.test.js file in each function folder.
Finally, since there is a discrepancy between what Stitch requires and what Jest requires, I have written a script to create a source code file under _source.js in each function folder.
So, each folder will contain these files (the underscore files will probably be ignored by Git, as they will always be generated):
_source.js
config.json
source.js
source.test.js
In order to create the underscored copies, I am using this shell script:
#!/bin/bash
# Copy all source.js files as _source.js
for f in $(find functions/ -name source.js); do cp -- "$f" "$(dirname $f)/_$(basename $f)"; done
# Search and replace in all _source.js files
for f in $(find functions/ -name _source.js); do sed -i -e 's/exports =/module.exports =/g' $f; done
A bit hacky perhaps, but it works!
Being relatively new to Jest - and having just used it to good success in a pristine environment/project with no problems, yesterday I decided to give it a shot in an older Node.js environment (10.15.x).
Initially, it worked fine. Then I started pulling in older requires dependencies and it immediately started failing:
FAIL modules/reviews/review.jest.js
● Test suite failed to run
SyntaxError: /Users/darrin/src/tot/commons/index.js: 'return' outside of function (2:0)
1 | 'use strict';
> 2 | return (module.exports = {
| ^
3 | accessControl: require('./modules/accessControl'),
4 | about: require('./modules/about'),
5 | api: require('./modules/api'),
at Parser.raise (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:6325:17)
at Parser.parseReturnStatement (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:10190:12)
at Parser.parseStatementContent (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:9877:21)
at Parser.parseStatement (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:9829:17)
at Parser.parseBlockOrModuleBlockBody (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:10405:25)
at Parser.parseBlockBody (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:10392:10)
at Parser.parseTopLevel (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:9758:10)
at Parser.parse (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:11270:17)
at parse (node_modules/#babel/core/node_modules/#babel/parser/lib/index.js:11306:38)
at parser (node_modules/#babel/core/lib/transformation/normalize-file.js:170:34)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.111s
It was news to me that returns at the global level (in these dependencies) aren't strictly okay in JavaScript, but it makes sense. Still... I cannot change these libraries, so how do I get around this error so I can use Jest?
Obviously, we don't have any interest in waiting for people to 'fix' libraries that return at global scope, so using Jest means I need a way to ignore those.
Here's what I ended up doing:
Add a "jest" configuration to file package.json:
"jest": {
"transform": {
"^.+\\.js$": "<rootDir>/.jest.transform.js"
}
},
Then in the root directory for the project I created file .jest.transform.js and placed this bit to tell Jest to tell Babel to allow return outside of functions:
const babelOptions = {
parserOpts: {
'allowReturnOutsideFunction': true
},
};
module.exports = require('babel-jest').createTransformer(babelOptions);
I hope this helps someone else - I almost walked away from Jest again because of this little nuance!
Error Snapshot Development when using ng-strict-di
I am developing project in meteor , If I don't use ng-strict-di in development it works fine but then in production(after making build ) I face following issue :
Error In Production Meteor
Development Error:
Error columnNumber: 12 fileName:
"localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6"
lineNumber: 895 message: "[$injector:strictdi] AdminCtrl is not using
explicit annotation and cannot be invoked in strict
mode\nerrors.angularjs.org/1.6.8/$injector/strictdi?p0=AdminCtrl"
stack:
"minErr/<#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:895:12\nannotate#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:5039:17\ninjectionArgs#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:5833:21\ninvoke#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:5868:18\n$controllerInit#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:11846:24\nnodeLinkFn#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:10709:35\ncompileTemplateUrl/<#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:11117:13\nprocessQueue#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:17939:37\nscheduleProcessQueue/<#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:17987:27\n$digest#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:19122:15\n$apply#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:19419:13\nbootstrapApply#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:2737:9\ninvoke#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:5876:16\ndoBootstrap#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:2735:5\nbootstrap#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:2755:12\nangularInit#localhost:3007/packages/modules.js?hash=f352b06ef868d8ef612a0e02d3fc1da778e4f6d6:2640:5\nangular.js/proto: Object { … }
Production(Build )
Error: [$injector:unpr] Unknown provider: tProvider <- t
errors.angularjs.org/1.6.8/$injector/unpr?p0=tProvider%20%3C-%20t
Stack trace:
r/<#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:7828
he/b.$injector<#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:30527
r#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:29436
he/E<#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:30610
r#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:29436
i#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:29741
a#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:29962
t#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:12:25404
p#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:12:11871
ht/<#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:12:17718
u#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:13:15438
l/<#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:13:15884
$digest#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:13:21599
$apply#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:13:23411
t#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:16603
a#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:30066
a#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:16522
lt#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:16810
ct#localhost:3008/7084ab3720bb9d2b53502cd2b81dce478c095b4c.js?meteor_js_resource=true:11:16046
angular.js/
You're bootstrapping Angular in strict dependency injection mode. This causes angular to throw exceptions when it encounters a dependency that isn't explicitly annotated. This is a way to enforce developers to guard against names of dependencies getting minified.
From the documentation:
If this attribute is present on the app element, the injector will be
created in "strict-di" mode. This means that the application will fail
to invoke functions which do not use explicit function annotation (and
are thus unsuitable for minification), as described in the Dependency
Injection guide, and useful debugging info will assist in tracking
down the root of these bugs.
The error tells you that you havn't explicitly annotated your AdminCtrl-controller. To fix this, your code should look similar to this:
angular.module('app').controller('AdminCtrl', ['firstDependency', 'secondDependency',
function(firstDependency, secondDependency){
// Controller body
}]);
or using $inject:
angular.module('app').controller('AdminCtrl', AdminCtrl);
AdminCtrl.$inject = ['firstDependency', 'secondDependency'];
function AdminCtrl(firstDependency, secondDependency){
// Controller body
}
The second method can be done for you automatically if you use build tools such as grunt, gulp or webpack, along with the ngAnnotate package.