Files
iNaturalistReactNative/tests/unit/components/ProjectDetails/ProjectDetails.test.js
Johannes Klein de05552286 Display proper error message if project is invite only (#3676)
* Move ProjectDetails to TS

* Remove flow type

* Type object properties

* Narrower type possible for helper function

* Type nav prop

* Type route params away from here

* Type route params away from here

* Type more project keys

* Type route params to Explore

* Type place generic for now

* Type function props

* Update displayProjectType.ts

* WIP: Add Alert to dummy codepath

* Move Alert to on button press, so before sheet is displayed

* Update string

* Update ProjectDetails.tsx

* Update ProjectDetailsContainer.tsx

* Remove flow type

* Type nav props

* Type ApiProject with fields we query for in ProjectDetails

Also check which ones are nullable according to API V2 docs

* Reorder fields

* Also request place_id field

This must be some kind of bug because the next query expects that we are asking for this value for the project.

* Apparently ProjectDetails sends an entire api result over navigation params

* Fix a TS error

* Same fix

* Replace second API call with data we can get from project call

* Fix a TS error

* Nullable properties according to API docs, do actually get back as null to us and not undefined

* Null passes through as prop

* I don't think those can be null

* Add other API query types

* Refactor to not pass id as function param

* Also query for membership_model

* Code style

* This seems nonsensical to me

Omit project_type and then put it back in

* Lift rule_preferences type to ApiProject

* Query for rule_preferences in Project details

* Minimal interface to use this function

* Reinstate previous interface

* Project now has a rule_preference field from container

* Be specific with what keys we prop down from container

* Route params are not optional because of a prior null check

* Update mock

* Add a TODO

* Add export
2026-06-02 11:06:38 +02:00

138 lines
3.9 KiB
JavaScript

import { screen } from "@testing-library/react-native";
import ProjectDetails from "components/ProjectDetails/ProjectDetails";
import ProjectDetailsContainer from "components/ProjectDetails/ProjectDetailsContainer";
import React from "react";
import factory from "tests/factory";
import faker from "tests/helpers/faker";
import { renderComponent, wrapInQueryClientContainer } from "tests/helpers/render";
const mockProject = factory( "RemoteProject", {
title: faker.lorem.sentence( ),
icon: faker.image.url( ),
header_image_url: faker.image.url( ),
description: faker.lorem.paragraph( ),
project_type: "collection",
user_ids: [faker.number.int( )],
} );
const mockProjectWithDateRange = factory( "RemoteProject", {
...mockProject,
rule_preferences: [
{
field: "d1",
value: "2024-03-07 07:42 -06:00",
},
{
field: "d2",
value: "2024-03-14 08:41 -07:00",
},
],
} );
jest.mock( "sharedHooks/useAuthenticatedQuery", ( ) => ( {
__esModule: true,
default: ( ) => ( {
data: mockProject,
} ),
} ) );
const mockMutate = jest.fn();
jest.mock( "sharedHooks/useAuthenticatedMutation", () => ( {
__esModule: true,
default: ( ) => ( {
mutate: mockMutate,
} ),
} ) );
jest.mock( "@react-navigation/native", ( ) => {
const actualNav = jest.requireActual( "@react-navigation/native" );
return {
...actualNav,
useRoute: ( ) => ( {
params: {
id: mockProject.id,
},
} ),
useNavigation: jest.fn( ),
};
} );
beforeAll( async () => {
jest.useFakeTimers( );
} );
describe( "ProjectDetails", ( ) => {
test( "should not have accessibility errors", async ( ) => {
const view = wrapInQueryClientContainer(
<ProjectDetailsContainer />,
);
// Disabled during the update to RN 0.78
expect( view ).toBeTruthy();
// expect( view ).toBeAccessible();
} );
test( "displays project details", ( ) => {
renderComponent( <ProjectDetailsContainer /> );
expect( screen.getByText( mockProject.title ) ).toBeTruthy( );
expect( screen.getByText( mockProject.description ) ).toBeTruthy( );
expect(
screen.getByTestId( "ProjectDetails.headerImage" ).props.source,
).toStrictEqual( { uri: mockProject.header_image_url } );
expect(
screen.getByTestId( "ProjectDetails.projectIcon" ).props.source,
).toStrictEqual( { uri: mockProject.icon } );
} );
it( "renders when project has no description", ( ) => {
renderComponent(
<ProjectDetails
project={{
...mockProject,
description: null,
}}
/>,
);
expect( screen.getByText( mockProject.title ) ).toBeTruthy( );
} );
test( "should display date range if collection project has date range", async ( ) => {
renderComponent( <ProjectDetails
project={mockProjectWithDateRange}
/> );
const dateRange = await screen.findByText( "Mar 7, 2024 - Mar 14, 2024" );
expect( dateRange ).toBeTruthy( );
} );
test( "should display date range if collection project has no date range", async ( ) => {
renderComponent( <ProjectDetails
project={mockProject}
/> );
const projectTypeText = await screen.findByText( /Collection Project/ );
expect( projectTypeText ).toBeTruthy( );
} );
test( "should display project type if project is traditional project", async ( ) => {
renderComponent( <ProjectDetails
project={{
...mockProjectWithDateRange,
project_type: "", // "" means "traditional"
}}
/> );
const projectTypeText = await screen.findByText( /Traditional Project/ );
expect( projectTypeText ).toBeTruthy( );
} );
test( "should display date range if umbrella project has date range", async ( ) => {
renderComponent( <ProjectDetails
project={{
...mockProjectWithDateRange,
project_type: "umbrella",
}}
/> );
const dateRange = await screen.findByText( "Mar 7, 2024 - Mar 14, 2024" );
expect( dateRange ).toBeTruthy( );
} );
} );