Skip to content

Commit

Permalink
WIP(to be cleaned) test: adding other interop tests
Browse files Browse the repository at this point in the history
  • Loading branch information
divdavem committed Mar 1, 2025
1 parent afad5d3 commit e953832
Showing 1 changed file with 301 additions and 0 deletions.
301 changes: 301 additions & 0 deletions test/interop.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
import { describe, expect, it, vi } from 'vitest';
import { Store, asReadable, batch, computed, writable } from '../src/index';
import { setActiveConsumer, type Signal, type Watcher } from '../src/interop';
import * as simpleLib from './adapters/simple';

describe('watch', () => {
it('should work', () => {
const onUseCalls: { onUnused: number }[] = [];
const store = writable(0, {
onUse: () => {
const call = { onUnused: 0 };
onUseCalls.push(call);
return () => {
call.onUnused++;
};
},
});
const notify = vi.fn();
const watcher = store.watchSignal(notify);
watcher.start();
expect(watcher.isUpToDate()).toBe(false);
expect(onUseCalls.length).toBe(0);
expect(watcher.update()).toBe(true);
expect(onUseCalls.length).toBe(1);
expect(onUseCalls[0].onUnused).toBe(0);
expect(watcher.isUpToDate()).toBe(true);
expect(watcher.get()).toBe(0);
expect(notify).not.toHaveBeenCalled();
store.set(1);
expect(notify).toHaveBeenCalledOnce();
notify.mockClear();
expect(watcher.isUpToDate()).toBe(false);
store.set(2);
expect(notify).not.toHaveBeenCalled();
expect(watcher.update()).toBe(true);
expect(watcher.isUpToDate()).toBe(true);
expect(watcher.get()).toBe(2);
expect(notify).not.toHaveBeenCalled();
store.set(3);
expect(notify).toHaveBeenCalledOnce();
expect(watcher.isUpToDate()).toBe(false);
notify.mockClear();
store.set(4);
store.set(2);
expect(notify).not.toHaveBeenCalled();
expect(watcher.update()).toBe(false);
expect(watcher.isUpToDate()).toBe(true);
expect(watcher.update()).toBe(false);
expect(watcher.get()).toBe(2);
watcher.stop();
expect(() => watcher.get()).toThrowError('invalid watcher state');
});
});

describe('fromWatch', () => {
it('should work', () => {
let notify: () => void;
let isUpdated = true;
let value = 0;
const watcher = {
isUpToDate: vi.fn(() => {
throw new Error('unexpected call to isUpToDate');
}),
isStarted: vi.fn(() => {
throw new Error('unexpected call to isStarted');
}),
update: vi.fn(() => isUpdated),
get: vi.fn(() => value),
start: vi.fn(),
stop: vi.fn(),
} satisfies Watcher<number>;
const watchFn = vi.fn((notifyFn: () => void) => {
notify = notifyFn;
return watcher;
}) satisfies (notify: () => void) => Watcher<number>;
const clearMocks = () => {
watchFn.mockClear();
watcher.update.mockClear();
watcher.get.mockClear();
watcher.stop.mockClear();
};
const store = asReadable({ watchSignal: watchFn });
expect(watchFn).toHaveBeenCalledOnce();
expect(store.get()).toBe(0);
expect(watchFn).toHaveBeenCalledOnce();
expect(watcher.update).toHaveBeenCalledOnce();
expect(watcher.get).toHaveBeenCalledOnce();
expect(watcher.stop).toHaveBeenCalledOnce();
clearMocks();
const values: number[] = [];
const unsubscribe = store.subscribe((value) => {
values.push(value);
});
expect(values).toEqual([0]);
expect(watcher.update).toHaveBeenCalledOnce();
expect(watcher.get).toHaveBeenCalledOnce();
expect(watchFn).not.toHaveBeenCalled();
expect(watcher.stop).not.toHaveBeenCalled();
clearMocks();
isUpdated = false;
batch(() => {
notify!();
});
expect(watcher.update).toHaveBeenCalledOnce();
expect(watchFn).not.toHaveBeenCalled();
expect(watcher.get).not.toHaveBeenCalled();
expect(watcher.stop).not.toHaveBeenCalled();
clearMocks();
expect(values).toEqual([0]);
isUpdated = true;
value = 2;
batch(() => {
notify!();
});
expect(watcher.update).toHaveBeenCalledOnce();
expect(watcher.get).toHaveBeenCalledOnce();
expect(watcher.stop).not.toHaveBeenCalled();
expect(watchFn).not.toHaveBeenCalled();
clearMocks();
expect(values).toEqual([0, 2]);
isUpdated = true;
watcher.get.mockImplementation(() => {
throw new Error('myerror');
});
expect(() => {
batch(() => {
notify!();
});
}).toThrowError('myerror');
expect(watcher.update).toHaveBeenCalledOnce();
expect(watcher.get).toHaveBeenCalledOnce();
expect(watcher.stop).not.toHaveBeenCalled();
expect(watchFn).not.toHaveBeenCalled();
clearMocks();
expect(() => {
store.get();
}).toThrowError('myerror');
expect(values).toEqual([0, 2]);
expect(watcher.update).not.toHaveBeenCalled();
expect(watcher.get).not.toHaveBeenCalled();
expect(watcher.stop).not.toHaveBeenCalled();
expect(watchFn).not.toHaveBeenCalled();
unsubscribe();
expect(watcher.stop).toHaveBeenCalledOnce();
expect(watcher.update).not.toHaveBeenCalled();
expect(watcher.get).not.toHaveBeenCalled();
expect(watchFn).not.toHaveBeenCalled();
expect(watcher.isUpToDate).not.toHaveBeenCalled();
});
});

describe('watch / fromWatch', () => {
it('should work to convert back and forth a basic writable', () => {
const store = writable(0);
const otherStore = asReadable({ watchSignal: (notify) => store.watchSignal(notify) });
expect(otherStore()).toBe(0);
store.set(1);
expect(otherStore()).toBe(1);
store.set(2);
expect(otherStore()).toBe(2);

const values: number[] = [];
const unsubscribe = otherStore.subscribe((value) => {
values.push(value);
});
expect(values).toEqual([2]);
store.set(3);
expect(values).toEqual([2, 3]);
expect(otherStore()).toBe(3);
batch(() => {
store.set(4);
expect(otherStore()).toBe(4);
store.set(5);
store.set(3);
});
expect(values).toEqual([2, 3]);
unsubscribe();
});

it('should work to convert back and forth a basic Store', () => {
class MyStore extends Store<number> {
increase() {
this.update((value) => value + 1);
}
}
const store = new MyStore(0);

const otherStore = asReadable({ watchSignal: (notify) => store.watchSignal(notify) });
expect(otherStore()).toBe(0);
store.increase();
expect(otherStore()).toBe(1);
store.increase();
expect(otherStore()).toBe(2);

const values: number[] = [];
const unsubscribe = otherStore.subscribe((value) => {
values.push(value);
});
expect(values).toEqual([2]);
store.increase();
expect(values).toEqual([2, 3]);
expect(otherStore()).toBe(3);
batch(() => {
store.increase();
expect(otherStore()).toBe(4);
store.increase();
store.increase();
});
expect(values).toEqual([2, 3, 6]);
unsubscribe();
});

it('should work to convert back and forth a computed', () => {
const store = writable(0);
const doubleStore = computed(() => store() * 2);
const otherStore = asReadable({
watchSignal: (notify) => doubleStore.watchSignal(notify),
});
expect(otherStore()).toBe(0);
store.set(1);
expect(otherStore()).toBe(2);
store.set(2);
expect(otherStore()).toBe(4);

const values: number[] = [];
const unsubscribe = otherStore.subscribe((value) => {
values.push(value);
});
expect(values).toEqual([4]);
store.set(3);
expect(values).toEqual([4, 6]);
expect(otherStore()).toBe(6);
batch(() => {
store.set(4);
expect(otherStore()).toBe(8);
store.set(5);
store.set(3);
});
expect(values).toEqual([4, 6]);
unsubscribe();
});
});

describe('interop computed', () => {
it('should work with a computed from another library', () => {
const a = writable(0);
const notify = vi.fn();
const watchers: Watcher<any>[] = [];
const consumer = {
addProducer: vi.fn(<T>(signal: Signal<T>) => {
const watcher = signal.watchSignal(notify);
watchers.push(watcher);
watcher.start();
watcher.update();
}),
};
const computeValue = () => {
const prevConsumer = setActiveConsumer(consumer);
try {
return 2 * a();
} finally {
setActiveConsumer(prevConsumer);
}
};
expect(computeValue()).toBe(0);
expect(consumer.addProducer).toHaveBeenCalledOnce();
expect(notify).not.toHaveBeenCalled();
a.set(1);
expect(notify).toHaveBeenCalledOnce();
expect(watchers).toHaveLength(1);
});

it('should work with a store from another library', () => {
const a = simpleLib.signal(0);
const myComputed = computed(() => a.get());
console.log('before myComputed()');
expect(myComputed()).toBe(0);
console.log('before a.set(1)');
a.set(1);
console.log('before myComputed()');
expect(myComputed()).toBe(1);
});

it('should work with a store from another library (live)', () => {
const a = simpleLib.signal(0);
const myComputed = computed(() => a.get());
const values: number[] = [];
console.log('before subscribe()');
myComputed.subscribe((value) => {
values.push(value);
});
expect(values).toEqual([0]);
console.log('before myComputed()');
expect(myComputed()).toBe(0);
console.log('before a.set(1)');
a.set(1);
expect(values).toEqual([0, 1]);
console.log('before myComputed()');
expect(myComputed()).toBe(1);
});
});

0 comments on commit e953832

Please sign in to comment.