/** * @jest-environment ./jest-environment-jsdom-fix */ import type { DIDDataStore } from '@glazed/did-datastore' import { jest } from '@jest/globals' import { PublicID } from '@self.id/core' import type { EthereumAuthProvider, SelfID, WebClientParams } from '@self.id/web' import { act, renderHook } from '@testing-library/react-hooks' import { Provider as JotaiProvider } from 'jotai' import React from 'react' import type { ReactNode } from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { Provider, ReactClient, connectionAtom, stateScope, useClient, usePublicRecord, useViewerConnection, useViewerID, useViewerRecord, } from '../src' type ChildrenProps = { children: ReactNode } describe('hooks', () => { const wrapper = ({ children }: ChildrenProps) => {children} test('useClient() returns the ReactClient instance', () => { const { result } = renderHook(() => useClient(), { wrapper }) expect(result.current).toBeInstanceOf(ReactClient) }) describe('useViewerID()', () => { test('returns null if there is no known viewer', () => { const { result } = renderHook(() => useViewerID(), { wrapper }) expect(result.current).toBeNull() }) test('returns a PublicID of the known viewer', () => { const stateWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result } = renderHook(() => useViewerID(), { wrapper: stateWrapper }) expect(result.current).toBeInstanceOf(PublicID) expect(result.current?.id).toBe('did:test:123') }) }) describe('useViewerConnection()', () => { test('with successful connection', async () => { const selfID = { id: 'did:test:123' } const authenticate = jest.fn(() => Promise.resolve(selfID as SelfID)) const client = new ReactClient({} as WebClientParams) client.authenticate = authenticate const clientWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result, waitForValueToChange } = renderHook(() => useViewerConnection(), { wrapper: clientWrapper, }) expect(result.current[0]).toEqual({ status: 'idle' }) act(() => { void result.current[1]({} as EthereumAuthProvider) }) await waitForValueToChange(() => result.current[0].status === 'connected') expect(authenticate).toBeCalledTimes(1) expect(result.current[0]).toEqual({ status: 'connected', selfID }) }) test('with authentication error', async () => { const error = new Error('Failed') const authenticate = jest.fn(() => Promise.reject(error)) const client = new ReactClient({} as WebClientParams) client.authenticate = authenticate const clientWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result, waitForValueToChange } = renderHook(() => useViewerConnection(), { wrapper: clientWrapper, }) expect(result.current[0]).toEqual({ status: 'idle' }) act(() => { void result.current[1]({} as EthereumAuthProvider) }) await waitForValueToChange(() => result.current[0].status === 'failed') expect(result.current[0]).toEqual({ status: 'failed', error }) }) }) describe('useViewerRecord()', () => { test('with no viewer', () => { const { result } = renderHook(() => useViewerRecord('basicProfile'), { wrapper }) expect(result.current).toEqual({ isLoadable: false, isLoading: false, isError: false, isMutable: false, isMutating: false, }) }) test('with PublicID viewer', async () => { const profile = { name: 'Alice' } const get = jest.fn(() => profile) const client = new ReactClient({} as WebClientParams) client._dataStore = { get } as unknown as DIDDataStore const stateWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result, waitForValueToChange } = renderHook(() => useViewerRecord('basicProfile'), { wrapper: stateWrapper, }) expect(result.current).toEqual({ content: undefined, error: null, isLoadable: true, isLoading: true, isError: false, isMutable: false, isMutating: false, set: expect.any(Function), merge: expect.any(Function), }) await waitForValueToChange(() => !result.current.isLoading) expect(result.current).toEqual({ content: profile, error: null, isLoadable: true, isLoading: false, isError: false, isMutable: false, isMutating: false, set: expect.any(Function), merge: expect.any(Function), }) }) test('with SelfID viewer', async () => { const profile = { name: 'Alice' } const get = jest.fn(() => profile) const set = jest.fn() const client = new QueryClient() const stateWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result, waitForValueToChange } = renderHook(() => useViewerRecord('basicProfile'), { wrapper: stateWrapper, }) expect(result.current).toEqual({ content: undefined, error: null, isLoadable: true, isLoading: true, isError: false, isMutable: true, isMutating: false, set: expect.any(Function), merge: expect.any(Function), }) await waitForValueToChange(() => !result.current.isLoading) expect(result.current).toEqual({ content: profile, error: null, isLoadable: true, isLoading: false, isError: false, isMutable: true, isMutating: false, set: expect.any(Function), merge: expect.any(Function), }) act(() => { if (result.current.isMutable) { void result.current.set({ name: 'Bob' }) } }) await waitForValueToChange(() => !result.current.isMutating) expect(set).toBeCalledTimes(1) expect(result.current).toEqual({ content: { name: 'Bob' }, error: null, isLoadable: true, isLoading: false, isError: false, isMutable: true, isMutating: false, set: expect.any(Function), merge: expect.any(Function), }) }) }) test('usePublicRecord()', async () => { const profile = { name: 'Alice' } const get = jest.fn(() => profile) const client = new ReactClient({} as WebClientParams) client._dataStore = { get } as unknown as DIDDataStore const clientWrapper = ({ children }: ChildrenProps) => ( {children} ) const { result, waitForValueToChange } = renderHook( () => usePublicRecord('basicProfile', 'did:test:123'), { wrapper: clientWrapper } ) expect(result.current).toEqual({ content: undefined, isLoading: true, isError: false, error: null, }) await waitForValueToChange(() => !result.current.isLoading) expect(result.current).toEqual({ content: profile, isLoading: false, isError: false, error: null, }) }) })