Open therealpaulgg opened 4 years ago
I was able to get it to work by doing import * as moment from "moment"
in Jest, but that makes my application not work. So clearly something is going on with the transpilation.
Hey @therealpaulgg these types of issues come up a lot but they're always related to Jest, generally with how Jest resolves modules and transforms them. Jest only uses svelte-jester
when it finds a file it doesn't understand such as .svelte
and it then usessvelte-jester
to transform that code to JS, but Jest is still responsible for resolving modules. By default Jest only resolves CommonJS by looking at the main
key in package.json
, and it's why import * as moment from "moment"
works, and it's also why it fails resolving modules fails a lot.
Anyway ... that was to help you potentially debug in the future but in your case you're using TypeScript to resolve modules so ... the first thing I'd say is you can remove babel-jest
and let ts-jest
resolve modules:
transform: {
// ...
"^.+\\.(js|ts)$": "ts-jest"
}
The second is you should configure TypeScript to use the node
module resolution strategy, there's a great section in the handbook about it.
"compilerOptions": {
// ...
"moduleResolution": "node"
}
Try these changes, if it doesn't still work let me know if you're receiving any new errors or the same.
Still having the same issue. I've changed my config (removed the extends svelte tsconfig portion, wasnt sure if that was messing with compilerOptions):
tsconfig.json:
{
"include": ["src/**/*", "src/types/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"],
"compilerOptions": {
"types": ["jest", "svelte"],
"allowSyntheticDefaultImports": true,
"target": "ES2019",
"moduleResolution": "node",
"allowJs": true,
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
jest.config.js:
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
transform: {
"^.+\\.svelte$": [
"svelte-jester",
{
preprocess: true
}
],
"^.+\\.(js|ts)$": "ts-jest"
},
moduleFileExtensions: ["js", "ts", "svelte"],
setupFilesAfterEnv: ["./jestSetup.ts"],
transformIgnorePatterns: [
"node_modules/(?!(svelte-typewriter|svelte-flatpickr)/)"
],
moduleNameMapper: {
"\\.(css|less|scss)$": "identity-obj-proxy"
},
globals: {
"ts-jest": {
isolatedModules: true
}
},
testPathIgnorePatterns: ["/lib/", "/node_modules/"]
}
Also worth noting I guess, since it's weird, I am using flatpickr as well for something using the format import flatpickr from "flatpickr"
. Tried doing import * as moment from "moment", so that stage of jest clears. and then I get (when doing flatpickr.parseDate(...)):
TypeError: Cannot read property 'parseDate' of undefined
Oh there's few more steps to configure ts-jest
, add allowJs: "true"
to your tsconfig
compilerOptions
and then set the preset in jest.config.js
to the following ts-jest/presets/js-with-ts
. Let me know if it's still the same after that.
If you also want the original Svelte files when using Jest then it's a good idea to use jest-svelte-resolver
.
Same deal 😕
also not totally how to use jest-svelte-resolver but it gave me an error basically immediately when parsing file:
export { default as Router } from "./Router.svelte";
^^^^^^
SyntaxError: Unexpected token 'export
That really sucks @therealpaulgg, so we don't go back and forth too much, when I have some time today I'll set up a project just like yours, and I'll see if I can get it working.
Hey @therealpaulgg I tried to recreate the issue but I can't. Everything works on my end. Can you give me an example of some file where you're trying the things that are failing? Some demo Svelte component with whatever you're importing and using would be awesome.
Yes in fact I can send you the entire project setup I'm using...
My test in test/integration
is rather 'dumb' so to speak and doesn't really do a whole lot. It loads the entire App.svelte obviously to make sure everything works. I just added the DatePicker.svelte component which imports moment and flatpickr and things like that. There shouldn't be any import errors, but in its current state I doubt the test will pass, just kind of threw the code in there. As long as I can get to a point where the error isn't related to bad imports, that's great.
Okay so first thing we now know is that moment
has issues with Jest resolution, not sure why ... There is this hack to make it work. I'd like to recommend this library as an alternative: https://github.com/iamkun/dayjs. Same API but only 2KB.
Now need to figure out why Typewriter.svelte
is not getting transformed.
So Jest always resolves based on the main
key in the package.json
and that's why svelte-flatpickr
is resolved as a CJS module and it only works if we do import * as Flatpickr from 'svelte-flatpickr
. You can always open up node_modules/{package}/package.json
and have a look at the exports to see what's being resolved. If you look inside node_modules/svelte-flatpickr/package.json
you'll see there is both a main
and svelte
export. Jest is defaulting to main
. If we want the svelte
file then we have to use a resolver like the one I linked earlier. Simply install it and add this line "resolver": "jest-svelte-resolver"
in jest.config.js
.
Finally, Typewriter
was failing because you added node_modules
to testPathIgnorePatterns
in jest.config.js
. This means Jest won't transform the file and ignores it, then at runtime it will fail because it doesn't understand .svelte
files.
Overall here's what you can do:
npm install @tsconfig/svelte
and change your tsconfig
to:{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["src/**"],
"exclude": ["node_modules/**"],
"compilerOptions": {
"types": ["jest"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
}
}
jest.config.js
to:module.exports = {
preset: "ts-jest/presets/js-with-ts",
globals: {
'ts-jest': {
// Let's avoid type checking during tests (performance boost).
diagnostics: false
}
},
transform: {
"^.+\\.svelte": [
"svelte-jester",
{
preprocess: true
}
],
"^.+\\.(js|ts)": "ts-jest"
},
moduleFileExtensions: ["svelte", "js", "ts"],
setupFilesAfterEnv: ["./jestSetup.ts"],
transformIgnorePatterns: [
"node_modules/(?!svelte-typewriter|svelte-flatpickr)/",
],
moduleNameMapper: {
"\\.(css|less|scss)$": "identity-obj-proxy"
}
}
Swap moment
for dayjs
.
Do import * as Flatpickr from 'svelte-flatpickr
or setup jest-resolver
and do import Flatpickr from 'svelte-flatpickr
.
Ok, thanks! tried all that. I am now getting dayjs is not a function
. dunno if you were able to test that or not, but its somehow the exact same problem as moment.
Ah it might be, I didn't check it and I cleaned up the project. Does import * as dayjs from 'dayjs'
do the trick or same issue as moment where you lose the ability to call a function?
same problem as moment, my whole project complains when i do any import like that
That hack I linked earlier might do the trick then if moment
and dayjs
are tricky:
import * as dayjs from "dayjs";
const dayjs = require("dayjs").default || require("dayjs");
Hey guys, is there any update on this? I'm also experiencing the same issue when I try to use the clsx
module. The error is kinda similar: clsx is not a function
. Have you @therealpaulgg solve your dayjs is not a function
error?
@ngavinsir unfortunately I don't recall this ever being solved for me. I have not worked on my project that required svelte in a while. Not sure if it is indicative of a problem with svelte-jester or something else.
Ah, okay, thanks for your response :+1:
Hi @mihar-22, I have the same issue as @therealpaulgg (except I use svelte-calendar
, not svelte-flatpickr
).
following the instructions above, I got the svelte-calendar issue fixed by adding the resolver.
but, the solution about dayjs does not work:
the trick const dayjs = require("dayjs").default || require("dayjs");
does fix the issue with jest, but the same code will cause a problem in browser with an error require is not defined
, because require() does not exist in the browser/client-side JavaScript.
Do you have any workaround for dayjs? thanks
A nasty workaround for clsx
:
In your package.json
:
"jest": {
...
"moduleNameMapper": {
"^clsx$": [
"<rootDir>/src/clsx.jest.js"
]
}
}
Create a new file /src/clsx.jest.js
const clsx = require('clsx/dist/clsx.js')
module.exports.default = clsx
Hey guys, is there any update on this? I'm also experiencing the same issue when I try to use the
clsx
module. The error is kinda similar:clsx is not a function
. Have you @therealpaulgg solve yourdayjs is not a function
error?
Can you please retry with the latest version and jest 27+?
Hi! I had the same problem, the hack I came up with is:
In my test setup script ( setupFilesAfterEnv: ["./jestSetup.ts"] in jest.config.js)
jest.mock('moment', () => ({ _esModule: true, default: jest.requireActual('moment'), }));
Hope it helps
A nasty workaround for
clsx
:
- In your
package.json
:"jest": { ... "moduleNameMapper": { "^clsx$": [ "<rootDir>/src/clsx.jest.js" ] } }
- Create a new file
/src/clsx.jest.js
const clsx = require('clsx/dist/clsx.js') module.exports.default = clsx
That's the only solution which works for me. I use it for my troubles with Flatpickr:
In your package.json
:
moduleNameMapper: {
"^flatpickr$": ["<rootDir>/src/flatpickr.jest.js"]
}
and then you make a new file ''/src/flatpickr.jest.js:
const flatpickr = require('flatpickr/dist/flatpickr.js')
module.exports.default = flatpickr
Thank you @wgrabias
It seems like TypeScript isn't the real culprit here. @sebastianrothe I've created a reproduction repository with the latest versions of Jest and svelte-jester to demonstrate that the issue persists.
What's confusing about this issue is that if one uses svelte-add-jest
to add Jest to a SvelteKit application, such issues won't come up because this adder generates code that runs Jest in experimental ESM mode. It's all good, but then the user-event
part of the Testing Library doesn't work, so it's a no-go zone for proper testing.
We have some tests with click-events
, also. What is not working on your side?
@sebastianrothe never mind, turns out it's an issue with @testing-library/user-event
(https://github.com/testing-library/user-event/issues/757#issuecomment-1009837483). I've created a reproduction of it in the esm
branch of the same repository in case you'd like to take a look.
However, I would personally prefer not using the experimental ES module support. Is there any chance this could be fixed inside of svelte-jester
to enable both modes?
We do ship both versions (CJS and ESM). You may have to manually override the transformer in your jest.config
:
transformer: "svelte-jester/dist/transformer.cjs",
You may have to manually override the transformer in your
jest.config
:
This configuration didn't resolve the issue in the reproduction repository:
export default {
testEnvironment: "jsdom",
transform: {
"^.+\\.js$": "babel-jest",
"^.+\\.svelte$": "svelte-jester/dist/transformer.cjs"
},
moduleFileExtensions: ["js", "svelte"]
};
Did I understand you correctly?
Yes, thats correct. But, I saw that your project is ESM (https://github.com/illright/jest-clsx-repro/blob/main/package.json#L21).
Jest is the culprit, sorry. Jest needs the experimental flag to work in ESM mode(https://jestjs.io/docs/ecmascript-modules). Node understands ESM natively.
I have a typescript Svelte component that makes use of moment.JS, importing like so:
import moment from "moment
It works with my rollup compilation and development server flawlessly. However, when using svelte-jester and using preprocessing, it says that 'moment is not a function'.
Here is my jest configuration:
jestSetup.ts:
tsconfig.json (have tried many variants):
test output:
This isn't the first time I have had trouble with transpiling. There have been issues with getting other Svelte components to work, problems with
flatpickr
, etc.Please let me know if there is something I am doing wrong.