import { renderHook } from "@testing-library/react"; import React from "react"; import { molecule, moleculeInterface } from "../vanilla"; import { MoleculeProvider } from "./MoleculeProvider"; import { strictModeSuite } from "./testing/strictModeSuite"; import { useMolecule } from "./useMolecule"; import { useInjector } from "./useInjector"; // Test molecules and interfaces const NumberInterface = moleculeInterface(); const StringInterface = moleculeInterface(); const NumberMolecule = molecule(() => 42); const AnotherNumberMolecule = molecule(() => 100); const StringMolecule = molecule(() => "hello world"); strictModeSuite(({ wrapper: Outer, isStrict }) => { describe("MoleculeProvider", () => { test("provides molecule interface implementation to children", () => { const TestComponent = () => { const number = useMolecule(NumberInterface); return { number }; }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; const { result } = renderHook(TestComponent, { wrapper: Wrapper, }); expect(result.current.number).toBe(42); }); test("provides different implementations for different interfaces", () => { const TestComponent = () => { const number = useMolecule(NumberInterface); const string = useMolecule(StringInterface); return { number, string }; }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; const { result } = renderHook(TestComponent, { wrapper: Wrapper, }); expect(result.current.number).toBe(42); expect(result.current.string).toBe("hello world"); }); test("nested providers override parent implementations", () => { const TestComponent = () => { const number = useMolecule(NumberInterface); return { number }; }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; const { result } = renderHook(TestComponent, { wrapper: Wrapper, }); expect(result.current.number).toBe(100); }); test("provider without value prop should not provide interface", () => { const TestComponent = () => { const injector = useInjector(); expect(() => { injector.get(NumberInterface); }).toThrowError( "Unbound molecule interface. Could not find a molecule.", ); }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; renderHook(TestComponent, { wrapper: Wrapper, }); }); test("provides molecule interface implementation to children molecules", () => { const ChildMolecule = molecule((get) => { return get(NumberInterface) + 1; }); const TestComponent = () => { const value = useMolecule(ChildMolecule); return { value }; }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; const { result } = renderHook(TestComponent, { wrapper: Wrapper, }); expect(result.current.value).toBe(43); }); test("preserves parent injector cache across multiple providers", () => { let sharedMoleculeCreationCount = 0; const SharedMolecule = molecule(() => { sharedMoleculeCreationCount++; return { id: sharedMoleculeCreationCount }; }); const InnerMolecule = molecule((get) => { const shared = get(SharedMolecule); const number = get(NumberInterface); return { shared, number }; }); // Component outside any provider const OuterComponent = () => { const shared = useMolecule(SharedMolecule); return
Outer: {shared.id}
; }; // Component inside nested providers const InnerComponent = () => { const service = useMolecule(InnerMolecule); return { sharedId: service.shared.id, number: service.number }; }; const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( {children} ); }; const { result } = renderHook(InnerComponent, { wrapper: Wrapper, }); // SharedMolecule should be created only once despite being used across providers expect(sharedMoleculeCreationCount).toBe(1); expect(result.current.sharedId).toBe(1); expect(result.current.number).toBe(42); }); }); });