Skip to content

$effect not working in tests, but $derived does #15051

@rChaoz

Description

@rChaoz
Contributor

Describe the bug

I'm having this weird issue where $effect is not working in tests. Interestingly, $derived does work, which means Svelte is not compiling in SSR mode. For example, in this test:

let num = $state(0)
let doubled = $derived(num * 2)
num = 5
expect(doubled).toBe(10)

$effect.root(() => {
    let a = 0
    $effect(() => {
        a = 5
    })
    flushSync()
    expect(a).toBe(5)
})

it errors at the very end: expected +0 to be 5, but it should work. Here's my Vite config:

export default defineConfig({
    plugins: [
        enhancedImages(),
        sveltekit(),
        ...
    ],
    test: {
        include: ["src/**/*.test.{js,ts}"],
        coverage: { ... },
        environmentMatchGlobs: [["src/**/*.svelte.test.{js,ts}", "jsdom"]],
        setupFiles: "src/vitest.setup.ts",
    },
    build: { ... },
})

I also have this mock in my global setup as I use if (browser) in other places in my tests:

vi.mock(import("esm-env"), async (importOriginal): Promise<typeof import("esm-env")> => {
    const module = await importOriginal()
    if (typeof window == "object")
        return {
            ...module,
            BROWSER: true,
        }
    else return module
})

I tried removing it, but without success. I'm not quite sure how to reproduce this yet. Is there something obviously wrong with my config, or something I can try?

Activity

changed the title [-]`$effect` not working in tests[/-] [+]`$effect` not working in tests, but `$derived` does[/+] on Jan 18, 2025
7nik

7nik commented on Jan 18, 2025

@7nik
Contributor

$derived does work, which means Svelte is not compiling in SSR mode

It means nothing - $derived works for both client and server, though it isn't reactive on the server.

Did you read the docs about testing?

rChaoz

rChaoz commented on Jan 18, 2025

@rChaoz
ContributorAuthor

though it isn't reactive on the server

Yes, that's what I meant. Isn't it reactive in my example above?

paoloricciuti

paoloricciuti commented on Jan 18, 2025

@paoloricciuti
Member

though it isn't reactive on the server

This could be fixed by #14977 but I think you should change your resolve conditions to browser if you also want to test $effect

rChaoz

rChaoz commented on Jan 18, 2025

@rChaoz
ContributorAuthor

This could be fixed by #14977

I don't need that, as I mentioned it is reactive for me.

I think you should change your resolve conditions to browser

I tried doing that, but I'm getting blasted with various errors, like

Error [ERR_REQUIRE_ESM]: require() of ES Module [...]\node_modules\@asamuzakjp\css-color\dist\esm\css-color.min.js from [...]\node_modules\cssstyle\lib\parsers.js not supported.
Instead change the require of css-color.min.js in [...]\node_modules\cssstyle\lib\parsers.js to a dynamic import() which is available in all CommonJS modules.

also, this makes every server test to fail.

paoloricciuti

paoloricciuti commented on Jan 18, 2025

@paoloricciuti
Member

Do you have other tests other than those? You need to run the svelte ones with condition browser and the others with condition node...take a look at this repo

https://github.com/dominikg/vitest-example-svelte5

rChaoz

rChaoz commented on Jan 18, 2025

@rChaoz
ContributorAuthor

I will take a look at that repo, although not sure if it will help, running just a single file with the test in my original post fails with the errors I mentioned. Also, I managed to get the transformed module, which looks good I think:

let num = __vite_ssr_import_0__.state(0);
let doubled = __vite_ssr_import_0__.derived(() => __vite_ssr_import_0__.get(num) * 2);

__vite_ssr_import_0__.set(num, 5);
__vite_ssr_import_1__.expect(__vite_ssr_import_0__.get(doubled)).toBe(10);

__vite_ssr_import_0__.effect_root(() => {
	let a = 0;

	__vite_ssr_import_0__.user_effect(() => {
		a = 5;
	});

	__vite_ssr_import_3__.flushSync();
	__vite_ssr_import_1__.expect(a).toBe(5);
});

Update: actually, I think I figured it out. Looking at the top of the generated module, I see this:

const __vite_ssr_import_0__ = await __vite_ssr_import__("/node_modules/svelte/src/internal/client/index.js");
const __vite_ssr_import_1__ = await __vite_ssr_import__("/node_modules/vitest/dist/index.js", {"importedNames":["vi","expect","it","describe"]});
const __vite_ssr_import_3__ = await __vite_ssr_import__("/node_modules/svelte/src/index-server.js", {"importedNames":["flushSync"]});

so, runes are transformed correctly, but flushSync() is imported from the server module, which is actually a no-op. I'll keep looking into this.

rChaoz

rChaoz commented on Jan 19, 2025

@rChaoz
ContributorAuthor

After upgrading to Vitest 3, I finally managed to get this working, using the example repo from above, with the workspace config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @rChaoz@paoloricciuti@7nik

        Issue actions

          `$effect` not working in tests, but `$derived` does · Issue #15051 · sveltejs/svelte