From 91ef244ef3c3a38d572501f49c996b33b1e77937 Mon Sep 17 00:00:00 2001 From: littensy <56808540+littensy@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:31:14 -0800 Subject: [PATCH] test: add important vue reactivity tests --- .../charm/src/__tests__/computed.test.luau | 101 ++++++++++++++++++ packages/charm/src/__tests__/effect.test.luau | 11 ++ 2 files changed, 112 insertions(+) diff --git a/packages/charm/src/__tests__/computed.test.luau b/packages/charm/src/__tests__/computed.test.luau index 691a7d9..4b11f0c 100644 --- a/packages/charm/src/__tests__/computed.test.luau +++ b/packages/charm/src/__tests__/computed.test.luau @@ -1,6 +1,7 @@ local Charm = require("../") local test = require("../../../../tests/test") +local atom = Charm.atom local computed = Charm.computed local effect = Charm.effect local signal = Charm.signal @@ -102,4 +103,104 @@ test("should receive previous value", function() assert(table.concat(calls) == "024", `should only call on even updates, got {table.concat(calls)}`) end) +-- https://github.com/vuejs/core/blob/minor/packages/reactivity/__tests__/computed.spec.ts#L875 +test("should chained computeds keep reactivity when computed effect happens", function() + local v = atom("Hello") + local c = computed(function() + v(v() .. " World") + return v() + end) + local d = computed(function() + return c() + end) + local e = computed(function() + return d() + end) + local res = computed(function() + return d() .. " | " .. e() + end) + + assert(res() == "Hello World | Hello World", res()) + + v(v() .. " World") + assert(res() == "Hello World World World World | Hello World World World World", res()) +end) + +-- https://github.com/vuejs/core/blob/minor/packages/reactivity/__tests__/computed.spec.ts#L580 +test("should not trigger effect scheduler by recursive computed effect", function() + local v = atom("Hello") + local c = computed(function() + v(v() .. " World") + return v() + end) + local comp = computed(function() + return c() + end) + + assert(comp() == "Hello World", comp()) + + v(v() .. " World") + assert(comp() == "Hello World World World World", comp()) +end) + +-- https://github.com/vuejs/core/blob/minor/packages/reactivity/__tests__/computed.spec.ts#L528 +test("should not override queried MaybeDirty result", function() + local Item = { + new = function() + return { v = atom(0) } + end, + } + local v1 = atom(nil :: any) + local v2 = atom(false) + local c1 = computed(function() + local c = v1() + if not v1() then + c = Item.new() + v1(c) + end + return c.v() + end) + local c2 = computed(function() + if not v2() then + return "no" + end + return if c1() then "yes" else "no" + end) + local c3 = computed(function() + return c2() + end) + + c3() + v2(true) + + c3() + v1().v(999) + + assert(c3() == "yes", "should return correct value after update") +end) + +-- https://github.com/vuejs/core/blob/minor/packages/reactivity/__tests__/computed.spec.ts#L503 +test("should trigger effect even computed already dirty", function() + local calls: { string } = {} + local v = atom(0) + local c1 = computed(function() + if v() == 0 then + v(1) + end + return "foo" + end) + local c2 = computed(function() + return v() .. c1() + end) + + effect(function() + table.insert(calls, c2()) + end) + assert(table.concat(calls, ",") == "0foo", table.concat(calls, ",")) + assert(v() == 1, v()) + v(2) + assert(table.concat(calls, ",") == "0foo,2foo", table.concat(calls, ",")) + assert(v() == 2, v()) +end) + return {} diff --git a/packages/charm/src/__tests__/effect.test.luau b/packages/charm/src/__tests__/effect.test.luau index b142ec8..79ef57f 100644 --- a/packages/charm/src/__tests__/effect.test.luau +++ b/packages/charm/src/__tests__/effect.test.luau @@ -387,4 +387,15 @@ test("should run cleanups outside any evaluation context", function() assert(calls == 0, "should not run after b update") end) +test("should avoid infinite recursive loops with multiple effects", function() + local counter, setCounter = Charm.signal(0) + effect(function() + setCounter(counter() + 1) + end) + effect(function() + counter() + end) + setCounter(counter() + 1) +end) + return {}