fix(vue): eslint/tsconfig migration + resolve type errors
@robonen/vue (toolkit): migrate to eslint flat config + composite tsconfig; fix composable + test type errors (writable computed returns, null guards, overload-compatible signatures, typed test helpers) — all type-level.
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { effectScope, nextTick, ref } from 'vue';
|
||||
import { watchIgnorable } from '.';
|
||||
|
||||
describe(watchIgnorable, () => {
|
||||
it('fires the callback on normal updates (async flush)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
watchIgnorable(count, cb);
|
||||
|
||||
count.value = 1;
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(1, 0, expect.any(Function));
|
||||
});
|
||||
|
||||
it('suppresses the callback inside ignoreUpdates (async flush)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignoreUpdates } = watchIgnorable(count, cb);
|
||||
|
||||
ignoreUpdates(() => {
|
||||
count.value = 1;
|
||||
});
|
||||
await nextTick();
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
count.value = 2;
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(2, 1, expect.any(Function));
|
||||
});
|
||||
|
||||
it('commits when an ignored update is followed by a real change before flush (async)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignoreUpdates } = watchIgnorable(count, cb);
|
||||
|
||||
ignoreUpdates(() => {
|
||||
count.value = 1;
|
||||
});
|
||||
// A real (non-ignored) change after the ignored one: syncCounter > ignoreCounter
|
||||
count.value = 2;
|
||||
await nextTick();
|
||||
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(2, 0, expect.any(Function));
|
||||
});
|
||||
|
||||
it('supports multiple chained ignored updates (async)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignoreUpdates } = watchIgnorable(count, cb);
|
||||
|
||||
ignoreUpdates(() => {
|
||||
count.value = 1;
|
||||
count.value = 2;
|
||||
count.value = 3;
|
||||
});
|
||||
await nextTick();
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fires the callback on normal updates (sync flush)', () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
watchIgnorable(count, cb, { flush: 'sync' });
|
||||
|
||||
count.value = 1;
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(1, 0, expect.any(Function));
|
||||
});
|
||||
|
||||
it('suppresses the callback inside ignoreUpdates (sync flush)', () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignoreUpdates } = watchIgnorable(count, cb, { flush: 'sync' });
|
||||
|
||||
ignoreUpdates(() => {
|
||||
count.value = 1;
|
||||
count.value = 2;
|
||||
});
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
count.value = 3;
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(3, 2, expect.any(Function));
|
||||
});
|
||||
|
||||
it('ignorePrevAsyncUpdates suppresses already-queued changes (async)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignorePrevAsyncUpdates } = watchIgnorable(count, cb);
|
||||
|
||||
count.value = 1;
|
||||
// Drop the pending change before the async callback flushes
|
||||
ignorePrevAsyncUpdates();
|
||||
await nextTick();
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignorePrevAsyncUpdates only drops prior changes, not subsequent ones (async)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignorePrevAsyncUpdates } = watchIgnorable(count, cb);
|
||||
|
||||
count.value = 1;
|
||||
ignorePrevAsyncUpdates();
|
||||
count.value = 2;
|
||||
await nextTick();
|
||||
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(2, 0, expect.any(Function));
|
||||
});
|
||||
|
||||
it('ignorePrevAsyncUpdates is a no-op for sync flush', () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { ignorePrevAsyncUpdates } = watchIgnorable(count, cb, { flush: 'sync' });
|
||||
|
||||
count.value = 1;
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
// Calling after a sync change does nothing
|
||||
ignorePrevAsyncUpdates();
|
||||
count.value = 2;
|
||||
expect(cb).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('stop tears down the watcher (async flush)', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { stop } = watchIgnorable(count, cb);
|
||||
|
||||
stop();
|
||||
count.value = 1;
|
||||
await nextTick();
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('stop tears down the watcher (sync flush)', () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const { stop } = watchIgnorable(count, cb, { flush: 'sync' });
|
||||
|
||||
stop();
|
||||
count.value = 1;
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('watches an array of sources', async () => {
|
||||
const a = ref(0);
|
||||
const b = ref('x');
|
||||
const cb = vi.fn();
|
||||
const { ignoreUpdates } = watchIgnorable([a, b], cb);
|
||||
|
||||
a.value = 1;
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith([1, 'x'], [0, 'x'], expect.any(Function));
|
||||
|
||||
ignoreUpdates(() => {
|
||||
b.value = 'y';
|
||||
});
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('respects an eventFilter', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
// A filter that drops every invocation
|
||||
watchIgnorable(count, cb, { eventFilter: () => {} });
|
||||
|
||||
count.value = 1;
|
||||
await nextTick();
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('supports the immediate option', () => {
|
||||
const count = ref(5);
|
||||
const cb = vi.fn();
|
||||
watchIgnorable(count, cb, { immediate: true, flush: 'sync' });
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
expect(cb).toHaveBeenLastCalledWith(5, undefined, expect.any(Function));
|
||||
});
|
||||
|
||||
it('stops with the owning effect scope', async () => {
|
||||
const count = ref(0);
|
||||
const cb = vi.fn();
|
||||
const scope = effectScope();
|
||||
|
||||
scope.run(() => watchIgnorable(count, cb));
|
||||
|
||||
count.value = 1;
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
|
||||
scope.stop();
|
||||
count.value = 2;
|
||||
await nextTick();
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user