import { waitFor } from "@testing-library/react-native"; import inatjs from "inaturalistjs"; import safeRealmWrite from "sharedHelpers/safeRealmWrite"; import { useTaxon } from "sharedHooks"; import factory, { makeResponse } from "tests/factory"; import faker from "tests/helpers/faker"; import { renderHookInApp } from "tests/helpers/render"; import setupUniqueRealm from "tests/helpers/uniqueRealm"; // UNIQUE REALM SETUP const mockRealmIdentifier = __filename; const { mockRealmModelsIndex, uniqueRealmBeforeAll, uniqueRealmAfterAll } = setupUniqueRealm( mockRealmIdentifier, ); jest.mock( "realmModels/index", ( ) => mockRealmModelsIndex ); jest.mock( "providers/contexts", ( ) => { const originalModule = jest.requireActual( "providers/contexts" ); return { __esModule: true, ...originalModule, RealmContext: { ...originalModule.RealmContext, useRealm: ( ) => global.mockRealms[mockRealmIdentifier], }, }; } ); beforeAll( uniqueRealmBeforeAll ); afterAll( uniqueRealmAfterAll ); // /UNIQUE REALM SETUP const mockRemoteTaxon = factory( "RemoteTaxon", { default_photo: { url: faker.image.url( ), }, } ); const mockTaxon = factory( "LocalTaxon", { _syncedAt: faker.date.recent( { days: 1 } ), default_photo: { url: faker.image.url( ), }, } ); const mockOutdatedTaxon = factory( "LocalTaxon", { _syncedAt: faker.date.recent( { days: 1, refDate: "2024-01-01" } ), default_photo: { url: faker.image.url( ), }, } ); describe( "with local taxon", ( ) => { beforeEach( ( ) => { jest.restoreAllMocks( ); // Write mock taxon to realm safeRealmWrite( global.mockRealms[mockRealmIdentifier], ( ) => { global.mockRealms[mockRealmIdentifier].create( "Taxon", mockTaxon, "modified" ); }, "write mock taxon, useTaxon test" ); } ); it( "should return an object", ( ) => { const { result } = renderHookInApp( ( ) => useTaxon( mockTaxon ) ); expect( result.current ).toBeInstanceOf( Object ); } ); describe( "when there is a local taxon with taxon id", ( ) => { it( "should return local taxon with default photo", ( ) => { const { result } = renderHookInApp( ( ) => useTaxon( mockTaxon ) ); const { taxon: resultTaxon } = result.current; expect( resultTaxon ).toHaveProperty( "default_photo" ); expect( resultTaxon.default_photo.url ).toEqual( mockTaxon.default_photo.url ); } ); it( "should request a taxon from the API if the local copy is out of date", async ( ) => { inatjs.taxa.fetch.mockResolvedValue( makeResponse( [mockRemoteTaxon] ) ); safeRealmWrite( global.mockRealms[mockRealmIdentifier], ( ) => { global.mockRealms[mockRealmIdentifier].create( "Taxon", mockOutdatedTaxon, "modified" ); }, "write mock outdated taxon, useTaxon test" ); renderHookInApp( ( ) => useTaxon( mockOutdatedTaxon ) ); await waitFor( ( ) => expect( inatjs.taxa.fetch ).toHaveBeenCalled( ) ); } ); } ); } ); describe( "when there is no local taxon with taxon id", ( ) => { beforeAll( ( ) => { safeRealmWrite( global.mockRealms[mockRealmIdentifier], ( ) => { global.mockRealms[mockRealmIdentifier].deleteAll( ); }, "delete all realm, useTaxon test" ); } ); describe( "with fetchRemote: true", ( ) => { it( "should request the taxon from the API", async ( ) => { expect( global.mockRealms[mockRealmIdentifier].objectForPrimaryKey( "Taxon", mockTaxon.id ), ).toBeNull( ); renderHookInApp( ( ) => useTaxon( mockTaxon ) ); await waitFor( ( ) => expect( inatjs.taxa.fetch ).toHaveBeenCalled( ) ); } ); it( "should return the argument taxon if request fails", ( ) => { // I don't love this. While it kind of mocks at the edge of the code // we need to integrate, it doesn't test out API error handling code. // I tried mocking inatjs to make it throw, but that always seems to // result in a failure in the test, even though useQuery should catch // those errors. ~~~kueda20240305 jest.mock( "@tanstack/react-query", () => ( { useQuery: jest.fn( ( ) => ( { error: { }, } ) ), } ) ); const partialTaxon = { id: faker.number.int( ), foo: "bar" }; const { result } = renderHookInApp( ( ) => useTaxon( partialTaxon ) ); expect( result.current.taxon.foo ).toEqual( "bar" ); jest.unmock( "@tanstack/react-query" ); } ); it( "should return a taxon like a local taxon record if the request succeeds", async ( ) => { const { result } = renderHookInApp( ( ) => useTaxon( { id: mockTaxon.id } ) ); await waitFor( ( ) => expect( result.current.taxon ).toHaveProperty( "default_photo" ) ); } ); } ); describe( "with fetchRemote: false", ( ) => { it( "should not call the API and return passed in taxon", ( ) => { const taxonId = faker.number.int( ); expect( global.mockRealms[mockRealmIdentifier].objectForPrimaryKey( "Taxon", taxonId ), ).toBeNull( ); const { result } = renderHookInApp( ( ) => useTaxon( { id: taxonId, foo: "bar" }, false ) ); expect( result.current.taxon ).not.toHaveProperty( "default_photo" ); expect( result.current.taxon.foo ).toEqual( "bar" ); } ); } ); } );