@dhmk/atom
Version:
Lightweight mobx-like observable values, computed values and side-effects
221 lines (220 loc) • 7.19 kB
JavaScript
import { atom, observe, act } from "./";
describe("atom", function () {
test("basic", function () {
var a = atom(1);
var b = atom(function () { return a() + 1; });
expect(a()).toEqual(1);
expect(b()).toEqual(2);
act(function () { return a.set(2); });
expect(a()).toEqual(2);
expect(b()).toEqual(3);
});
test("oldValue eq newValue == don`t invalidate children", function () {
var a = atom(1);
var b = atom(function () { return a() % 2; });
var spy = jest.fn();
var c = atom(function () { return spy(b()); });
observe(c); // if comment this, total called times should be 4
c(); // b = 1
expect(spy).toBeCalledTimes(1);
act(function () { return a.set(3); }); // b = 1
c();
expect(spy).toBeCalledTimes(1);
act(function () { return a.set(5); }); // b = 0
c();
act(function () { return a.set(6); }); // b = 0
c();
expect(spy).toBeCalledTimes(2);
});
test("diamond", function () {
var computeSpy = jest.fn();
var observeSpy = jest.fn();
var a = atom(1);
var b = atom(function () { return a() + 1; });
var c = atom(function () { return a() * 10; });
var d = atom(function () {
computeSpy();
return b() + c();
});
observe(function () {
d();
observeSpy();
});
expect(d()).toEqual(12);
expect(computeSpy).toBeCalledTimes(1);
expect(observeSpy).toBeCalledTimes(1);
act(function () { return a.set(2); });
expect(d()).toEqual(23);
expect(computeSpy).toBeCalledTimes(2);
expect(observeSpy).toBeCalledTimes(2);
});
test("onBO/onBUO", function () {
var onBOspy = jest.fn();
var onBUOspy = jest.fn();
var a = atom(1, {
onBecomeObserved: onBOspy,
onBecomeUnobserved: onBUOspy,
});
expect(onBOspy).toBeCalledTimes(0);
expect(onBUOspy).toBeCalledTimes(0);
// irrelevant
act(function () { return a.set(2); });
expect(onBOspy).toBeCalledTimes(0);
expect(onBUOspy).toBeCalledTimes(0);
// BO
var d = observe(a);
expect(onBOspy).toBeCalledTimes(1);
expect(onBUOspy).toBeCalledTimes(0);
// irrelevant
act(function () { return a.set(3); });
expect(onBOspy).toBeCalledTimes(1);
expect(onBUOspy).toBeCalledTimes(0);
// BUO
d();
expect(onBOspy).toBeCalledTimes(1);
expect(onBUOspy).toBeCalledTimes(1);
// irrelevant
d();
expect(onBOspy).toBeCalledTimes(1);
expect(onBUOspy).toBeCalledTimes(1);
// irrelevant
act(function () { return a.set(4); });
expect(onBOspy).toBeCalledTimes(1);
expect(onBUOspy).toBeCalledTimes(1);
// BO
var d2 = observe(a);
expect(onBOspy).toBeCalledTimes(2);
expect(onBUOspy).toBeCalledTimes(1);
// BUO
d2();
expect(onBOspy).toBeCalledTimes(2);
expect(onBUOspy).toBeCalledTimes(2);
});
});
describe("observe", function () {
test("basic", function () {
var a = atom(1);
var b = atom(2);
var spy = jest.fn();
observe(function () {
a() + b();
spy();
});
expect(spy).toBeCalledTimes(1);
act(function () { return a.set(2); });
act(function () { return b.set(3); });
expect(spy).toBeCalledTimes(3);
});
test("re-runs if atoms changed in-flight", function () {
var a = atom(1);
var spy = jest.fn();
observe(function () {
if (a() === 1)
a.set(2);
if (a() === 10)
a.set(11);
spy();
});
expect(spy).toBeCalledTimes(2);
act(function () { return a.set(3); });
expect(spy).toBeCalledTimes(3);
act(function () { return a.set(10); });
expect(spy).toBeCalledTimes(5);
var b = atom(1);
var c = atom(function () { return b().toString().length; });
var once = false;
observe(function () {
if (!once) {
once = true;
b.set(2);
}
c();
spy();
});
expect(spy).toHaveBeenCalledTimes(6);
});
test("dispose", function () {
var a = atom(1);
var spy = jest.fn();
observe(function (d) {
a();
d();
spy();
});
act(function () { return a.set(2); });
act(function () { return a.set(3); });
expect(spy).toBeCalledTimes(1);
var d = observe(function () {
a();
spy();
});
d();
act(function () { return a.set(4); });
act(function () { return a.set(5); });
expect(spy).toBeCalledTimes(2);
});
test("custom async scheduler", function (done) {
var a = atom(1);
var spy = jest.fn();
var sch = function (x) { return x(); };
observe(function () {
if (a() === 11) {
// the end
// 1 - initial pass
// 2 - after a.set(2)
// 3 - after a.set(10)
expect(spy).toBeCalledTimes(3);
done();
}
if (a() === 1) {
a.set(2);
setTimeout(function () {
// 1. now, observe will run outside `runPendingAtoms` (and batching)
sch = function (x) { return setTimeout(x); };
act(function () { return a.set(10); });
});
}
if (a() === 10) {
// 2. still outside `runPendingAtoms`
// call `observe` immediately
// this will fail if g_batchCount === 0
sch = function (x) { return x(); };
a.set(11);
}
spy();
}, {
scheduler: function (run) { return sch(run); },
});
});
test("force run in batch", function () {
var a = atom(1);
var spy = jest.fn();
var d = observe(function () {
a();
spy();
});
expect(spy).toBeCalledTimes(1);
act(function () {
d.invalidate( /*false*/);
expect(spy).toBeCalledTimes(1);
d.invalidate( /*true*/);
expect(spy).toBeCalledTimes(1);
});
expect(spy).toBeCalledTimes(2);
});
});
test("action", function () {
var a = atom(1);
var b = atom(2);
var spy = jest.fn();
observe(function () {
a() + b();
spy();
});
expect(spy).toBeCalledTimes(1);
act(function () {
a.set(2);
b.set(3);
});
expect(spy).toBeCalledTimes(2);
});