Skip to content
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

Svelte 5: unit testing of $derived arry fails after testing $derived array length #12708

Closed
Lenostatos opened this issue Aug 2, 2024 · 3 comments · Fixed by #12747
Closed
Assignees
Labels
Milestone

Comments

@Lenostatos
Copy link

Describe the bug

Dear svelte 5 team,

I am experiencing an issue with vitest when unit testing a class with $derived fields.

I am using Svelte 5.0.0-next.206 with svelteKit and have modified my vite.config.ts to include (hopefully) all necessary configurations:

import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vitest/config";
import basicSsl from "@vitejs/plugin-basic-ssl";

export default defineConfig({
  plugins: [sveltekit(), basicSsl()],
  test: {
    include: ["src/**/*.{test,spec}.{js,ts}"],
    environment: "jsdom",
  },
  resolve: process.env.VITEST
    ? {
        conditions: ["browser"],
      }
    : undefined,
});

The class which I want to test with vitest contains two $state arrays and one $derived array which is a combination of the other two:

// classWDerivedCombiArray.svelte.ts
export class ClassWDerivedCombiArray {
  // two simple arrays as $state
  private _stateArray1 = $state<number[]>([]);
  private _stateArray2 = $state<number[]>([]);

  // one array that combines the previous two with $derived
  private _derivedCombiArray = $derived([
    ...this._stateArray1,
    ...this._stateArray2,
  ]);

  // the $derived length of the combined array
  private _derivedCombiArrayLength = $derived(this._derivedCombiArray.length);

  public get derivedCombiArray() {
    return this._derivedCombiArray;
  }

  public get derivedCombiArrayLength() {
    return this._derivedCombiArrayLength;
  }

  public pushToStateArray1() {
    this._stateArray1.push(1);
  }

  public pushToStateArray2() {
    this._stateArray2.push(2);
  }
}

This works perfectly well in the playground.

However, the testing behaves weirdly. When I test the $derived length of the combined array, the $derived combined array doesn't update anymore:

// classWDerivedCombiArray.test.ts
import { describe, it, expect } from "vitest";
import { ClassWDerivedCombiArray } from "./classWDerivedCombiArray.svelte";

describe("test class with derived state", () => {
  it("reactively updates its state", () => {
    const obj = new ClassWDerivedCombiArray();

    expect(obj.derivedCombiArray).toEqual([]);

    // expect(obj.derivedCombiArrayLength).toEqual(0); // <-- If this test is executed, the next test below fails.

    obj.pushToStateArray1();

    // if the length test above is executed, this test fails with vitest
    // seeing obj.derivedCombiArray as [] instead of [1]
    expect(obj.derivedCombiArray).toEqual([1]);

    // expect(obj.derivedCombiArrayLength).toEqual(1); // <-- If this test is executed, the next test below fails.

    obj.pushToStateArray2();

    // if the length test above is executed, this test fails with vitest
    // seeing obj.derivedCombiArray as [1] instead of [1, 2]
    expect(obj.derivedCombiArray).toEqual([1, 2]);

    // ...the pattern goes on with every other push to one of the state arrays
  });
});

Am I missing something or is this actually a bug?

Thank you very much for your time and work!

Reproduction

see this Stackblitz

Logs

No response

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.20.3 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    svelte: ^5.0.0-next.206 => 5.0.0-next.206

Severity

annoyance

@trueadm
Copy link
Contributor

trueadm commented Aug 4, 2024

Just a quick question, why are you doing:

private _derivedCombiArrayLength = $derived(this._derivedCombiArray.length);

When instead you can do:

expect(obj.derivedCombiArray.length).toEqual(0);

I'll take a bigger look into this tomorrow :)

@trueadm trueadm self-assigned this Aug 4, 2024
@Lenostatos
Copy link
Author

Lenostatos commented Aug 5, 2024

Just a quick question, why are you doing:

private _derivedCombiArrayLength = $derived(this._derivedCombiArray.length);

When instead you can do:

expect(obj.derivedCombiArray.length).toEqual(0);

I'll take a bigger look into this tomorrow :)

Ah, yeah, I also noticed, that that works. I was wondering whether that was because property access via a $derived doesn't work in vitest, or whether this is because of svelte 5's reactive array implementation? Maybe vitest switches back to the default array implementation if that .length property is accessed?

Thank you very much for looking into this!

@trueadm trueadm added the bug label Aug 6, 2024
@trueadm trueadm added this to the 5.0 milestone Aug 6, 2024
@trueadm
Copy link
Contributor

trueadm commented Aug 6, 2024

Nice this found a bug in our reactivity logic. Should have a fix up soon :)

Update: figured it out, this was a recent regression.

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

Successfully merging a pull request may close this issue.

2 participants