diff --git a/tests/unit/components/Match/PhotosSection.test.js b/tests/unit/components/Match/PhotosSection.test.js new file mode 100644 index 000000000..c099e0fd3 --- /dev/null +++ b/tests/unit/components/Match/PhotosSection.test.js @@ -0,0 +1,105 @@ +import { fireEvent, screen, waitFor } from "@testing-library/react-native"; +import PhotosSection from "components/Match/PhotosSection"; +import React from "react"; +import factory from "tests/factory"; +import { renderComponent } from "tests/helpers/render"; + +describe( "PhotosSection", () => { + const mockNavToTaxonDetails = jest.fn(); + + const defaultProps = { + representativePhoto: { ...factory( "RemotePhoto" ), ...{ id: 4 } }, + obsPhotos: [factory( "LocalObservationPhoto" )], + navToTaxonDetails: mockNavToTaxonDetails, + taxon: factory( "LocalTaxon", { + taxonPhotos: [ + { photo: factory( "RemotePhoto", { id: 1 } ) }, + { photo: factory( "RemotePhoto", { id: 2 } ) }, + { photo: factory( "RemotePhoto", { id: 3 } ) } + ] + } ) + }; + + it( "displays photo count when multiple observation photos exist", async () => { + const multipleObsPhotos = [ + factory( "LocalObservationPhoto" ), + factory( "LocalObservationPhoto" ), + factory( "LocalObservationPhoto" ) + ]; + + renderComponent( + + ); + + await waitFor( () => { + expect( screen.getByTestId( "MatchScreen.ObsPhoto" ) ).toBeVisible(); + } ); + expect( screen.getByText( "3" ) ).toBeVisible(); + } ); + + it( "hides taxon photos when hideTaxonPhotos is true", async () => { + renderComponent( + + ); + + await waitFor( () => { + expect( screen.getByTestId( "MatchScreen.ObsPhoto" ) ).toBeVisible(); + } ); + expect( screen.queryByTestId( "TaxonDetails.photo.1" ) ).toBeFalsy(); + expect( screen.queryByTestId( "TaxonDetails.photo.4" ) ).toBeFalsy(); + } ); + + // Not working due to known bug: https://linear.app/inaturalist/issue/MOB-1069/taxon-photos-hidden-for-iconic-taxa-match-screens + /* it( "does not render iconic taxon photos", async () => { + renderComponent( + + ); + + await waitFor( () => { + expect( screen.getByTestId( "TaxonDetails.photo.2" ) ).toBeVisible(); + } ); + expect( screen.queryByTestId( "TaxonDetails.photo.1" ) ).toBeFalsy(); + } ); */ + + it( "calls navToTaxonDetails prop when taxon photo is pressed", async () => { + renderComponent( + + ); + + await waitFor( () => { + expect( screen.getByTestId( "TaxonDetails.photo.4" ) ).toBeVisible(); + } ); + + const photoButton = screen.getByTestId( "TaxonDetails.photo.4" ); + fireEvent.press( photoButton ); + + expect( mockNavToTaxonDetails ).toHaveBeenCalled(); + } ); + + it( "limits displayed taxon photos to maximum of 3", async () => { + renderComponent( + + ); + + // Representative photo + 2 more = 3 total + await waitFor( () => { + expect( screen.getByTestId( "TaxonDetails.photo.4" ) ).toBeVisible(); + } ); + expect( screen.getByTestId( "TaxonDetails.photo.1" ) ).toBeVisible(); + expect( screen.getByTestId( "TaxonDetails.photo.2" ) ).toBeVisible(); + expect( screen.queryByTestId( "TaxonDetails.photo.3" ) ).toBeFalsy(); + } ); +} ); diff --git a/tests/unit/components/Match/PreMatchLoadingScreen.test.js b/tests/unit/components/Match/PreMatchLoadingScreen.test.js new file mode 100644 index 000000000..4505f1667 --- /dev/null +++ b/tests/unit/components/Match/PreMatchLoadingScreen.test.js @@ -0,0 +1,20 @@ +import { screen } from "@testing-library/react-native"; +import PreMatchLoadingScreen from "components/Match/PreMatchLoadingScreen"; +import React from "react"; +import { renderComponent } from "tests/helpers/render"; + +describe( "PreMatchLoadingScreen", () => { + it( "renders nothing when isLoading is false", () => { + renderComponent( ); + + expect( screen.queryByText( "Analyzing for the best identification..." ) ).toBeFalsy(); + } ); + + it( "shows loading screen when isLoading is true", () => { + renderComponent( ); + + expect( screen.getByText( "Analyzing for the best identification..." ) ).toBeVisible(); + // Find activity indicator + expect( screen.getByRole( "progressbar" ) ).toBeVisible(); + } ); +} ); diff --git a/tests/unit/components/Match/SaveDiscardButtons.test.js b/tests/unit/components/Match/SaveDiscardButtons.test.js new file mode 100644 index 000000000..510253c90 --- /dev/null +++ b/tests/unit/components/Match/SaveDiscardButtons.test.js @@ -0,0 +1,30 @@ +import { fireEvent, screen } from "@testing-library/react-native"; +import SaveDiscardButtons from "components/Match/SaveDiscardButtons"; +import React from "react"; +import { renderComponent } from "tests/helpers/render"; + +describe( "SaveDiscardButtons", () => { + it( "calls handlePress with 'save'", () => { + const mockHandlePress = jest.fn(); + + renderComponent( ); + + const saveButton = screen.getByText( "SAVE" ); + expect( saveButton ).toBeVisible(); + fireEvent.press( saveButton ); + + expect( mockHandlePress ).toHaveBeenCalledWith( "save" ); + } ); + + it( "calls handlePress with 'discard'", () => { + const mockHandlePress = jest.fn(); + + renderComponent( ); + + const discardButton = screen.getByText( "DISCARD" ); + expect( discardButton ).toBeVisible(); + fireEvent.press( discardButton ); + + expect( mockHandlePress ).toHaveBeenCalledWith( "discard" ); + } ); +} ); diff --git a/tests/unit/components/Match/helpers/tryToReplaceWithLocalTaxon.test.js b/tests/unit/components/Match/helpers/tryToReplaceWithLocalTaxon.test.js new file mode 100644 index 000000000..bb173c32a --- /dev/null +++ b/tests/unit/components/Match/helpers/tryToReplaceWithLocalTaxon.test.js @@ -0,0 +1,58 @@ +import tryToReplaceWithLocalTaxon from "components/Match/helpers/tryToReplaceWithLocalTaxon"; +import factory from "tests/factory"; + +describe( "tryToReplaceWithLocalTaxon", () => { + it( "should return original suggestion when local taxa array is empty", () => { + const localTaxa = []; + const suggestion = { + combined_score: 92, + taxon: factory( "RemoteTaxon", { id: 745 } ) + }; + + const result = tryToReplaceWithLocalTaxon( localTaxa, suggestion ); + + expect( result ).toEqual( suggestion ); + } ); + + it( "should return original suggestion when no matching local taxon is found", () => { + const localTaxa = [ + factory( "LocalTaxon", { id: 746, name: "Silphium laciniatum" } ), + factory( "LocalTaxon", { id: 747, name: "Silphium integrifolium" } ) + ]; + const suggestion = { + combined_score: 88, + taxon: factory( "RemoteTaxon", { id: 745, name: "Silphium perfoliatum" } ) + }; + + const result = tryToReplaceWithLocalTaxon( localTaxa, suggestion ); + + expect( result ).toEqual( suggestion ); + } ); + + it( "should merge local taxon data when matching taxon is found", () => { + const localTaxon = factory( "LocalTaxon", { + id: 745, + name: "Silphium perfoliatum", + preferred_common_name: "Cup Plant", + rank: "species", + rank_level: 10, + _synced_at: new Date( "2024-01-15" ), + representative_photo: [{ photo: { id: 678 } }] + } ); + const localTaxa = [localTaxon]; + const suggestion = { + combined_score: 95, + taxon: { + id: 745, + name: "Silphium perfoliatum", + taxon_photos: [{ photo: { id: 123 } }], + iconic_taxon_name: "Plantae", + representative_photo: [{ photo: { id: 456 } }] + } + }; + + const result = tryToReplaceWithLocalTaxon( localTaxa, suggestion ); + + expect( result ).toEqual( { ...suggestion, taxon: { ...suggestion.taxon, ...localTaxon } } ); + } ); +} );