Skip to content

Testing of rune effects is broken #16092

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
vits opened this issue Jun 6, 2025 · 9 comments
Open

Testing of rune effects is broken #16092

vits opened this issue Jun 6, 2025 · 9 comments
Milestone

Comments

@vits
Copy link

vits commented Jun 6, 2025

Describe the bug

Testing rune effects as described in Svelte documentation is broken in Svelte > 5.1.11 and is double broken with vitest 3.2.x

Repo has logger.svelte.test.js from documentation with latest svelte and vitest versions. Its expectation fails as flushSync() seems to be ignored and code in $effect rune is not run.

Downgrading vitest version to 3.1.4 changes test behavior - it fails with effect_update_depth_exceeded.

Downgrading Svelte as well to 5.1.11 or older makes test to pass.

Reproduction

Repo: https://github.com/vits/vitest-svelte-effects

Logs

Any version of Svelte 5 with vitest 3.2.x:

AssertionError: expected [] to deeply equal [ +0 ]

- Expected
+ Received

- [
-   0,
- ]
+ []

 ❯ src/logger.svelte.test.js:15:21
     13|   // use flushSync to execute all pending effects synchronously
     14|   flushSync();
     15|   expect(log.value).toEqual([0]);
       |                     ^
     16|
     17|   count = 1;
 ❯ update_reaction node_modules/.pnpm/[email protected]/node_modules/svelte/src/internal/client/runtime.js:414:56
 ❯ update_effect node_modules/.pnpm/[email protected]/node_modules/svelte/src/internal/client/runtime.js:580:18
 ❯ create_effect node_modules/.pnpm/[email protected]/node_modules/svelte/src/internal/client/reactivity/effects.js:118:17
 ❯ Module.effect_root node_modules/.pnpm/[email protected]/node_modules/svelte/src/internal/client/reactivity/effects.js:231:17
 ❯ src/logger.svelte.test.js:6:15

Svelte > 5.1.1 with vitest 3.1.4:

 ❯  client  src/logger.svelte.test.js (1 test | 1 failed) 9ms
   × Effect 8ms
     → effect_update_depth_exceeded
Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
https://svelte.dev/e/effect_update_depth_exceeded

System Info

System:
    OS: macOS 15.5
    CPU: (10) arm64 Apple M1 Pro
    Memory: 96.94 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.14.0 - ~/.local/share/mise/installs/node/22.14.0/bin/node
    npm: 10.9.2 - ~/.local/share/mise/installs/node/22.14.0/bin/npm
    pnpm: 9.15.7 - ~/Library/pnpm/pnpm
  Browsers:
    Chrome: 137.0.7151.69
    Edge: 137.0.3296.62
    Safari: 18.5
  npmPackages:
    svelte: ^5.33.14 => 5.33.14

Severity

annoyance

@microdou
Copy link

microdou commented Jun 6, 2025

Likely related: I'm encountering vitest failure from minimum project created by sv.

To reproduce:

  1. Create a minimum project with npx sv create. (I also use node adapter and pnpm here.)
  2. Update libraries in package.json to latest versions: "vitest": "3.2.2", and install the updates pnpm i.
  3. Run vitest pnpm run test:unit
  4. Test fails
 ❯ |client| src/routes/page.svelte.test.ts (1 test | 1 failed) 6ms
   × /+page.svelte > should render h1 5ms
     → lifecycle_function_unavailable
`mount(...)` is not available on the server
https://svelte.dev/e/lifecycle_function_unavailable

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  |client| src/routes/page.svelte.test.ts > /+page.svelte > should render h1
Svelte error: lifecycle_function_unavailable
`mount(...)` is not available on the server
https://svelte.dev/e/lifecycle_function_unavailable
 ❯ Module.lifecycle_function_unavailable node_modules/.pnpm/[email protected]/node_modules/svelte/src/internal/server/errors.js:9:16
      7|  */
      8| export function lifecycle_function_unavailable(name) {
      9|  const error = new Error(`lifecycle_function_unavailable\n\`${name}(..…
       |                ^
     10| 
     11|  error.name = 'Svelte error';
 ❯ Module.mount node_modules/.pnpm/[email protected]/node_modules/svelte/src/index-server.js:25:4
 ❯ mount node_modules/.pnpm/@[email protected][email protected][email protected]_@[email protected][email protected]_75bbe56ae1d328546c39894cf011ca14/node_modules/@testing-library/svelte/src/core/modern.svelte.js:27:28
 ❯ render node_modules/.pnpm/@[email protected][email protected][email protected]_@[email protected][email protected]_75bbe56ae1d328546c39894cf011ca14/node_modules/@testing-library/svelte/src/pure.js:75:26
 ❯ src/routes/page.svelte.test.ts:8:3

Test fails since [email protected]. Test works with [email protected].

@vits
Copy link
Author

vits commented Jun 6, 2025

This is related to #14239 so maybe should have added to existing issue. My bad. Hovewer solution to use environment: jsdom doesn't seem to work, as my repo is created with latest sv and there is environment: jsdom configuration for client workspace.

@em1nx
Copy link

em1nx commented Jun 7, 2025

Same issue, npm run test fails after upgrading vitest from 3.1.3 to 3.2.2

 DEPRECATED  The `workspace` option is deprecated and will be removed in the next major. To hide this warning, rename `workspace` option to `projects`.

 RUN  v3.2.2 /Users/emin/Sources/tmp/sv4

 ✓  server  src/demo.spec.ts (1 test) 1ms
 ❯  client  src/routes/page.svelte.test.ts (1 test | 1 failed) 5ms
   × /+page.svelte > should render h1 5ms
     → lifecycle_function_unavailable
`mount(...)` is not available on the server
https://svelte.dev/e/lifecycle_function_unavailable

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL   client  src/routes/page.svelte.test.ts > /+page.svelte > should render h1
Svelte error: lifecycle_function_unavailable
`mount(...)` is not available on the server
https://svelte.dev/e/lifecycle_function_unavailable
 ❯ Module.lifecycle_function_unavailable node_modules/svelte/src/internal/server/errors.js:9:16
      7|  */
      8| export function lifecycle_function_unavailable(name) {
      9|  const error = new Error(`lifecycle_function_unavailable\n\`${name}(...)\` is not available on the server\nhttps://svelte.dev/e/lifecycle_function_unavailable`);
       |                ^
     10| 
     11|  error.name = 'Svelte error';
 ❯ Module.mount node_modules/svelte/src/index-server.js:25:4
 ❯ mount node_modules/@testing-library/svelte/src/core/modern.svelte.js:27:28
 ❯ render node_modules/@testing-library/svelte/src/pure.js:75:26
 ❯ src/routes/page.svelte.test.ts:8:3

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯


 Test Files  1 failed | 1 passed (2)
      Tests  1 failed | 1 passed (2)
   Start at  16:32:52
   Duration  843ms (transform 191ms, setup 358ms, collect 37ms, tests 6ms, environment 260ms, prepare 79ms)

@elliott-with-the-longest-name-on-github
Copy link
Contributor

This appears to have been an issue in Vitest -- 3.2.3 fixes the issue for me. If you're still experiencing it after upgrading, feel free to reopen!

@elliott-with-the-longest-name-on-github elliott-with-the-longest-name-on-github closed this as not planned Won't fix, can't repro, duplicate, stale Jun 11, 2025
@vits
Copy link
Author

vits commented Jun 12, 2025

I have no permission to reopen this issue. But vitest 3.2.3 only changes problem back to state it was with vitest <= 3.1.4, as described at the end of my reproduction logs. I.e. logger.svelte.js test from Svelte documentation now fails with effect_update_depth_exceeded. Downgrading Svelte to 5.1.11 makes test pass.

❯  client  src/logger.svelte.test.js (1 test | 1 failed) 9ms
   × Effect 9ms
     → effect_update_depth_exceeded
Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
https://svelte.dev/e/effect_update_depth_exceeded

Updated my reproduction repo to latest Svelte and Vitest.

@elliott-with-the-longest-name-on-github
Copy link
Contributor

Will look into it!

@raythurnvoid
Copy link
Contributor

@vits You cannot used array.push inside an $effect.

push internally reads the length of the array, therefore when using it you are at the same time reading and writing to the same array.

This is a problem because the reading phase will add the array.length to the deps of the $effect and the writing phase will increase it causing the same $effect to be invalidated.

I think there are only 2 things that you can do safely in the $effect, either you console.log the length and push outside of it, or you can re-assign to a different value eg: log = Array.from({ length: getValue() }).map((_, i) => i);.

However it's important you don't write and read on the same variable, you can look at push as doing something like num = num + getValue() or log = [...log, getValue()], in both cases you can expect the infinite loop due to reading and writing on the same variable.

One possible workaround for user specific case is to wrap push into untrack, this will allow you to push but you also have to tell svelte that you want it to depend on the value returned by getValue().

The complete solution of your specific reactive module is

$effect(() => {
	const v = getValue();
	untrack(() => {
		log.push(v);
	});
});

This will ensure that your $effect will listen only to changes of your count state that is accessed via getValue but will ignore changes to log.

@vits
Copy link
Author

vits commented Jun 16, 2025

@raythurnvoid Thanks, your example with untrack works with latest Svelte and vitest.

So this is documentation issue, as the code example with array.push inside effect is from Svelte testing documentation, https://svelte.dev/docs/svelte/testing#Unit-and-integration-testing-using-Vitest-Using-runes-inside-your-test-files. Should be fixed there.

@elliott-with-the-longest-name-on-github
Copy link
Contributor

Ah @vits good catch, the docs should not be doing that. It sounds like there's not actually a bug here after all (after the Vite issue was fixed, at least).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants