87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
|
|
import {
|
||
|
|
describe,
|
||
|
|
expect,
|
||
|
|
it,
|
||
|
|
vi,
|
||
|
|
} from 'vitest';
|
||
|
|
import { createSingleton } from './createSingleton';
|
||
|
|
|
||
|
|
describe('createSingleton', () => {
|
||
|
|
it('does not call the factory until the first get (lazy)', () => {
|
||
|
|
const factory = vi.fn(() => ({ id: 1 }));
|
||
|
|
createSingleton(factory);
|
||
|
|
expect(factory).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('constructs on first get and memoizes the instance', () => {
|
||
|
|
const factory = vi.fn(() => ({ id: 1 }));
|
||
|
|
const singleton = createSingleton(factory);
|
||
|
|
|
||
|
|
const a = singleton.get();
|
||
|
|
const b = singleton.get();
|
||
|
|
|
||
|
|
expect(factory).toHaveBeenCalledTimes(1);
|
||
|
|
expect(a).toBe(b);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('rebuilds a fresh instance after reset', () => {
|
||
|
|
let count = 0;
|
||
|
|
const singleton = createSingleton(() => ({ id: ++count }));
|
||
|
|
|
||
|
|
const first = singleton.get();
|
||
|
|
singleton.reset();
|
||
|
|
const second = singleton.get();
|
||
|
|
|
||
|
|
expect(first).not.toBe(second);
|
||
|
|
expect(second.id).toBe(2);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('runs teardown once, with the live instance, on reset', () => {
|
||
|
|
const teardown = vi.fn();
|
||
|
|
const singleton = createSingleton(() => ({ id: 1 }), teardown);
|
||
|
|
|
||
|
|
const instance = singleton.get();
|
||
|
|
singleton.reset();
|
||
|
|
|
||
|
|
expect(teardown).toHaveBeenCalledTimes(1);
|
||
|
|
expect(teardown).toHaveBeenCalledWith(instance);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('treats reset before any get as a no-op (no teardown, no throw)', () => {
|
||
|
|
const teardown = vi.fn();
|
||
|
|
const singleton = createSingleton(() => ({ id: 1 }), teardown);
|
||
|
|
|
||
|
|
expect(() => singleton.reset()).not.toThrow();
|
||
|
|
expect(teardown).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('does not run teardown again on a second consecutive reset', () => {
|
||
|
|
const teardown = vi.fn();
|
||
|
|
const singleton = createSingleton(() => ({ id: 1 }), teardown);
|
||
|
|
|
||
|
|
singleton.get();
|
||
|
|
singleton.reset();
|
||
|
|
singleton.reset();
|
||
|
|
|
||
|
|
expect(teardown).toHaveBeenCalledTimes(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('works without a teardown', () => {
|
||
|
|
const singleton = createSingleton(() => ({ id: 1 }));
|
||
|
|
|
||
|
|
singleton.get();
|
||
|
|
expect(() => singleton.reset()).not.toThrow();
|
||
|
|
expect(singleton.get().id).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('caches a falsy instance value without re-running the factory', () => {
|
||
|
|
const factory = vi.fn(() => undefined);
|
||
|
|
const singleton = createSingleton<undefined>(factory);
|
||
|
|
|
||
|
|
singleton.get();
|
||
|
|
singleton.get();
|
||
|
|
|
||
|
|
expect(factory).toHaveBeenCalledTimes(1);
|
||
|
|
});
|
||
|
|
});
|