Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/smart-fireants-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codiume/hooks": patch
---

Update [use-singleton] tests
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:
id: changesets
uses: changesets/action@v1
with:
publish: pnpm run release
version: pnpm run changeset:version
publish: pnpm run changeset:publish

env:
NPM_CONFIG_PROVENANCE: true
GITHUB_TOKEN: ${{ github.token }}
Expand Down
15 changes: 4 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
"version": "0.0.7",
"description": "A collection of reusable react hooks for state and UI management",
"homepage": "https://github.com/codiume/hooks.git",
"keywords": [
"hooks",
"library",
"react",
"react-hooks",
"state"
],
"keywords": ["hooks", "library", "react", "react-hooks", "state"],
"author": "MHD <m@codiume.com>",
"contributors": [
{
Expand All @@ -21,9 +15,7 @@
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"files": ["dist"],
"exports": {
".": {
"import": {
Expand All @@ -43,7 +35,8 @@
"format": "biome format --write .",
"check": "biome check --write .",
"changeset": "changeset",
"release": "pnpm run build && changeset publish"
"changeset:version": "changeset version && pnpm run format",
"changeset:publish": "pnpm run build && changeset publish"
},
"license": "MIT",
"repository": {
Expand Down
69 changes: 57 additions & 12 deletions src/use-singleton/use-singleton.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,77 @@
import { renderHook } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';

import { useSingleton } from './use-singleton';

describe('useSingleton', () => {
it('should call the initializer function only once', () => {
const initializer = vi.fn(() => ({ value: 'singleton' }));

const { rerender } = renderHook(() => useSingleton(initializer));
it('should return the same value on multiple renders', () => {
const { result, rerender } = renderHook(() =>
useSingleton(() => ({ foo: 'bar' }))
);

expect(initializer).toHaveBeenCalledTimes(1);
const initialValue = result.current;

rerender();

expect(result.current).toBe(initialValue);
});

it('should only call the factory function once', () => {
const factoryFn = vi.fn(() => ({ foo: 'bar' }));

const { result, rerender } = renderHook(() => useSingleton(factoryFn));

expect(factoryFn).toHaveBeenCalledTimes(1);

rerender();

expect(initializer).toHaveBeenCalledTimes(1);
expect(factoryFn).toHaveBeenCalledTimes(1);
});

it('should work with primitive values', () => {
const { result } = renderHook(() => useSingleton(() => 42));

expect(result.current).toBe(42);
});

it('should return the same instance on re-renders', () => {
const initializer = () => ({ value: Math.random() });
it('should work with objects', () => {
const { result } = renderHook(() => useSingleton(() => ({ foo: 'bar' })));

expect(result.current).toEqual({ foo: 'bar' });
});

it('should work with functions', () => {
const fn = () => 'hello';
const { result } = renderHook(() => useSingleton(() => fn));

expect(result.current).toBe(fn);
expect(result.current()).toBe('hello');
});

const { result, rerender } = renderHook(() => useSingleton(initializer));
it('should maintain identity even if factory function creates new object each time', () => {
const { result, rerender } = renderHook(() =>
useSingleton(() => ({ random: Math.random() }))
);

const firstInstance = result.current;
const initialValue = result.current;

rerender();

const secondInstance = result.current;
expect(result.current).toBe(initialValue);
expect(result.current.random).toBe(initialValue.random);
});

it('should work with async factory functions', async () => {
const asyncFactory = async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
return { foo: 'bar' };
};

const { result } = renderHook(() => useSingleton(() => asyncFactory()));

expect(result.current).toBeInstanceOf(Promise);

expect(firstInstance).toBe(secondInstance);
const resolvedValue = await result.current;
expect(resolvedValue).toEqual({ foo: 'bar' });
});
});