solid-testing-library: Can't test component with Vitest
Hi,
I’m trying to test my SolidJS library with Vitest. But I’m getting the following error:
FAIL src/index.test.tsx [ src/index.test.tsx ]
SyntaxError: The requested module 'solid-js/web' does not provide an export named 'hydrate'
❯ async src/index.test.tsx:8:31
4| test('render', async () => {
5| await render(() => <div>Hello</div>)
6| })
| ^
7|
I’m using:
"solid-js": "^1.3.12"
"vite": "^2.8.6",
"vite-plugin-solid": "^2.2.6",
"vitest": "^0.6.1"
"solid-testing-library": "^0.3.0",
vite.config.js
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
export default defineConfig({
test: {
environment: 'jsdom',
transformMode: {
web: [/\.[jt]sx?$/],
},
// solid needs to be inline to work around
// a resolution issue in vitest:
deps: {
inline: [/solid-js/],
},
// if you have few tests, try commenting one
// or both out to improve performance:
// threads: false,
// isolate: false,
},
plugins: [solid()],
resolve: {
conditions: ['development', 'browser'],
},
})
I’m new to testing. Is my configuration correct or is it bug? I could not find a lot of documentation on testing SolidJS with Vitest.
Thanks.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 25 (1 by maintainers)
Commits related to this issue
- fix(js-vitest,ts-vitest): force vitest to transform solid-testing-library For why this is necessary, see solidjs/solid-testing-library#10. — committed to thislooksfun/templates by thislooksfun 2 years ago
- Force vitest to transform solid-testing-library For why this is necessary, see solidjs/solid-testing-library#10. — committed to thislooksfun/templates by thislooksfun 2 years ago
After hours and hours of debugging I finally figured out the issue! It’s been reported as https://github.com/vitest-dev/vitest/issues/1588. In the meantime, there is a workaround:
If you do that it will work 100% of the time, regardless of which package management tool you use (npm, yarn, pnpm, etc.)
Want to know why?
If you only care about fixing the issue then you’re done with this comment. If, on the other hand, you’re curious as to what actually happened, then read on.
Why the error
'solid-js/web' does not provide an export named 'hydrate'?First, some context. Vite, like many bundlers, supports a system called “conditional exports”, which allows you to define a set of exports from your package that should be used in different situations.
solid-js/webuses this system to export different files based on whether you are on the server or the client. This is useful for keeping bundle size down without having to manually specify different imports for every place the code will be used (especially problematic in ssr environments, where the same code runs on the client and the server), and ensuring that the code actually runs (client bundles can include browser APIs and polyfills, for example, while server code doesn’t need it).Solid’s
ts-vitesttemplate defines the resolve conditions to be["development", "browser"], which should import Solid via theexports.browser.development.importpath. When runningvitest, however, the wrong package gets imported. Instead ofexports.browser.development.import, the system instead importsexports.node.import. Since this is a server file it has no need for any client-side APIs, and thus doesn’t exporthydrateorrender.Why did it work with
pnpmthen?Ah, what an excellent question. Deep inside of Vitest’s module resolution code is a seemingly simple function:
_shouldExternalize(id). This function answers a simple question: do we need to transform the file before exporting it, or can we “externalize” the import (ask Node to do it for us). The logic in there isn’t too complex, but the part we care about is this line:if (matchExternalizePattern(id, depsExternal)) return id. This says “if the id (which is a filepath) matches any of thedepsExternalpatterns, then use Node’s defaultimport()rather than transforming the file ourselves”. The problem is that the defaultdepsExternalpatterns includes a match for/\.mjs$/, which means that any file that ends with.mjsis handed off to Node to resolve. This is a problem because this library, as @KaiHuebner guessed, exports a file namedsolid-testing-library/dist/index.mjs, which importshydrateandrenderfromsolid-js/web. When Vitest gets to this file it matches against thedepsExternalpattern which tells it to use Node’simport. Node then tries to resolve the import tosolid-js/webthe way it knows how: by importing the file defined in theexports.node.importkey. Since this is a server file, it doesn’t contain an export namedhydrate, and so the import fails.The reason it works when installed via pnpm is because of another check in earlier in
_shouldExternalize(). Vitest provides an option calleddepswhich allows you to override which modules should and should not be externalized. The second piece of the puzzle is again thets-vitesttemplate, which setsdeps.internalizeto[/solid-js/]. This is intended to forcesolid-jsto be transformed, rather than going through Node’simport, which is does, but only whensolid-jsis itself imported from a transformed file. The reason it works with pnpm though is because of the install paths. When usingnpm,solid-testing-libraryis imported fromnode_modules/solid-testing-library/dist/index.mjs. But when usingpnpmit’s imported fromnode_modules/.pnpm/solid-testing-library@0.3.0_solid-js@1.4.5/node_modules/solid-testing-library/dist/index.mjs. If you look closely, you can see that path contains “solid-js”, which means that it is caught by the/solid-js/regex and told to internalize. When installed vianpm, the path doesn’t containsolid-js, and thus doesn’t get told to externalize.This is why the short fix is to add
/solid-testing-library/to thedeps.internalizearray fixes the issue, it tellsvitestto always internalizesolid-testing-library, thus ensuring it reads the right exports and thus finds thehydrateand/orrenderfunctions it needs.@atk Not quite. In order for this to work we need
_shouldExternalize(id)to returnfalse. There are only 3 cases where that happens:idmatchesdeps.inlineidmatchesdefaultInlineOption 1 is the quick fix proposed above, but it requires manual configuration. Option 2 is out because we can’t influence
defaultInline, nor can we make the import be one of those options. So we’re left with option 3: bypass all the other checks.In order to reach the end of
_shouldExternalize()we need the following:isNodeBuiltin(id)must befalse– this is easy, since it’s not a node builtin.idmust NOT be adata:url – also easy, it’s a fileidmust NOT matchdeps.external– again, easyidmust NOT match thedepsExternalpattern – this means the file can’t end in.cjs.jsor.mjsidMUST NOT be in**/dist/**(easy) OR**/node_modules/**(impossible)isValidNodeImport(id)must returnfalseWe can control everything up to the final check, but since the file will always be inside of
node_moduleswe are forced to go a step further: ensure thatisValidNodeImport("path/to/solid-testing-library/index/file")returnsfalse. So lets look at when it does so:idhas a protocol that isn’t in the list of allowed protocols (defaults tonode:,file:, ordata:).js.es.jsor.esm.jsOption 1 is ruled out because we can’t control the protocol. Option 2 is possible, but the only extensions options that I know of are
.ts,.tsx,.js,.jsx,.cjsand.esm, and of those all but.cjsand.jsare already ruled out, and you can’t (or at least really shouldn’t) put ESM code in a.cjsfile, so that leaves just.js. Option 4 is also ruled out because just before it is a check forpackage.type === "module", which would cause it to returntrue.All of this comes together to mean that the only way I can see to get around this without needing to set
deps.inlinemanually is to have the file end in.esm.js. However I can’t comfortably recommend this solution. That behavior is several dependencies deep and is NOT one of the API guarantees of either package, so while it should work for now, I don’t feel confident that it will continue working in the future.I think the most reliable way forwards is to have prominent documentation here in
solid-testing-libraryshowing how to set it up withvitest(specifydeps.inlineeither to includesolid-testing library, to include all ofnode_modules, or to just betruewhich inlines everything), and hope that thevitestteam comes up with some solution on their end for this to be nicer in the future.I’ve run into the same issue and cannot use pnpm (I’m behind a corporate proxy on Windows, and can’t get it to install)
I would appreciate if this could be reopened and further investigated, please
I had the same issue with the ts/vitest template.
npm testwould fail. Tests pass with pnpm.I am investigating further, but this issue seems to be on the side of vitest, not solid.
Hi guys, thanks for the quick response.
Yes, I installed jsdom version 19.0.0.
I’m not using any server side rendering, so I actually don’t need the hydrate function.
I was thinking it has something to do with the import statement in:
node_modules/solid-testing-library/dist/index.mjsWhen I change line 2 to:
import { render as solidRender } from "solid-js/web";Then I get a different error message:
SyntaxError: The requested module 'solid-js/web' does not provide an export named 'render'