diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 89ab3462..15b329fb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ variables: value: "postgres:15.8-alpine" description: "Image de la base de données" PROJECT_VERSION: - value: "2.2.5" + value: "2.2.6" description: "Version du projet à déployer" SERVER_ENV_INT: value: "int-rapportnav-appli01" diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 52b0948b..43e0ab66 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.lang.System.getenv group = "fr.gouv.dgampa" -version = "2.2.5" +version = "2.2.6" description = "RapportNav" val kotlinVersion by extra("1.9.24") diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMIllegalFish.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMIllegalFish.kt index 5b24a6f1..0713504e 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMIllegalFish.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMIllegalFish.kt @@ -49,10 +49,16 @@ data class AEMIllegalFish( fun getNbrOfInfractionWithPV(fishActions: List): Double { return fishActions.map { it?.controlAction?.action }.fold(0.0) { acc, c -> - acc.plus(c!!.gearInfractions.filter { g -> g.infractionType == InfractionType.WITH_RECORD }.size) - .plus(c.otherInfractions.filter { o -> o.infractionType == InfractionType.WITH_RECORD }.size) - .plus(c.speciesInfractions.filter { s -> s.infractionType == InfractionType.WITH_RECORD }.size) - .plus(c.logbookInfractions.filter { l -> l.infractionType == InfractionType.WITH_RECORD }.size) + acc.plus(c?.gearInfractions?.filter { g -> g.infractionType == InfractionType.WITH_RECORD }?.size ?: 0) + .plus( + c?.otherInfractions?.filter { o -> o.infractionType == InfractionType.WITH_RECORD }?.size ?: 0 + ) + .plus( + c?.speciesInfractions?.filter { s -> s.infractionType == InfractionType.WITH_RECORD }?.size ?: 0 + ) + .plus( + c?.logbookInfractions?.filter { l -> l.infractionType == InfractionType.WITH_RECORD }?.size ?: 0 + ) }; } @@ -61,7 +67,7 @@ data class AEMIllegalFish( } fun getQuantityOfFish(fishActions: List): Double { - return 0.0; + return 0.0; //TODO: hasSomeSpeciesSeized } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMMigrationRescue.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMMigrationRescue.kt index 9782122a..01872702 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMMigrationRescue.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMMigrationRescue.kt @@ -32,15 +32,23 @@ data class AEMMigrationRescue( companion object { fun getNbrPersonsRescued(actionRescues: List): Double { - return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.numberPersonsRescued!!) } + return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.numberPersonsRescued ?: 0) } } fun getNbrOfVesselsTrackedWithoutIntervention(actionRescues: List): Double { - return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.nbOfVesselsTrackedWithoutIntervention!!) } + return actionRescues.fold(0.0) { acc, actionRescue -> + acc.plus( + actionRescue?.nbOfVesselsTrackedWithoutIntervention ?: 0 + ) + } } fun getAssistedVesselsReturningToShore(actionRescues: List): Double { - return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.nbAssistedVesselsReturningToShore!!) } + return actionRescues.fold(0.0) { acc, actionRescue -> + acc.plus( + actionRescue?.nbAssistedVesselsReturningToShore ?: 0 + ) + } } private fun actionRescueEntities(navActions: List): List { diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescue.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescue.kt index 81bafa98..005b3616 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescue.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescue.kt @@ -20,7 +20,7 @@ data class AEMOutOfMigrationRescue( companion object { fun getNbrPersonsRescued(actionRescues: List): Double { - return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.numberPersonsRescued!!) } + return actionRescues.fold(0.0) { acc, actionRescue -> acc.plus(actionRescue?.numberPersonsRescued ?: 0) } } private fun actionRescueEntities(navActions: List): List { diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt index 1e85632a..e2ea2678 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionId.kt @@ -8,8 +8,25 @@ import fr.gouv.dgampa.rapportnav.domain.repositories.mission.crew.IMissionCrewRe class GetAgentsCrewByMissionId(private val agentCrewRepository: IMissionCrewRepository) { fun execute(missionId: Int, commentDefaultsToString: Boolean? = false): List { + val rolePriority = listOf( + "Commandant", + "Second capitaine", + "Second", + "Chef mécanicien", + "Second mécanicien", + "Mécanicien électricien", + "Mécanicien", + "Chef de quart", + "Maître d’équipage", + "Agent pont", + "Agent machine", + "Agent mécanicien", + "Électricien", + "Cuisinier", + ) + return agentCrewRepository.findByMissionId(missionId = missionId) .map { it.toMissionCrewEntity(commentDefaultsToString) } - .sortedBy { it.id } + .sortedBy { rolePriority.indexOf(it.role.title) } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/crew/MissionCrewModel.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/crew/MissionCrewModel.kt index 89f0d34a..f89cf89c 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/crew/MissionCrewModel.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/crew/MissionCrewModel.kt @@ -19,7 +19,7 @@ class MissionCrewModel( var missionId: Int, @Column(name = "comment", nullable = true) - var comment: String?, + var comment: String? = null, @ManyToOne @JoinColumn(name = "agent_role_id") diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescueTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescueTest.kt index c63db3fa..5c08f125 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescueTest.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/entities/aem/AEMOutOfMigrationRescueTest.kt @@ -29,6 +29,30 @@ class AEMOutOfMigrationRescueTest { assertThat(migrationRescue.nbrOfRescuedOperation).isEqualTo(nbrOfRescuedOperation); } + @Test + fun `Should not thow null pointer exception event if nbrPersonsRescued is null`() { + val action = NavActionEntity( + id = UUID.randomUUID(), + missionId = 761, + actionType = ActionType.ILLEGAL_IMMIGRATION, + startDateTimeUtc = Instant.parse("2019-09-09T00:00:00.000+01:00"), + endDateTimeUtc = Instant.parse("2019-09-09T01:00:00.000+01:00"), + rescueAction = ActionRescueEntity( + missionId = 761, + id = UUID.randomUUID(), + startDateTimeUtc = Instant.parse("2019-09-08T22:00:00.000+01:00"), + endDateTimeUtc = Instant.parse("2019-09-09T01:00:00.000+01:00"), + observations = "", + numberPersonsRescued = null, + numberOfDeaths = 0, + isMigrationRescue = false + ) + ) + val migrationRescue = AEMOutOfMigrationRescue(navActions = listOf(action)); + assertThat(migrationRescue).isNotNull(); + assertThat(migrationRescue.nbrPersonsRescued).isEqualTo(0.0); + } + private fun navActionEntities(): List { val actions = listOf( NavActionEntity( diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionIdTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionIdTest.kt new file mode 100644 index 00000000..f171cccf --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/crew/GetAgentsCrewByMissionIdTest.kt @@ -0,0 +1,89 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.crew + +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.crew.IMissionCrewRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.crew.AgentModel +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.crew.AgentRoleModel +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.crew.MissionCrewModel +import junit.framework.TestCase.assertEquals +import org.junit.jupiter.api.Test +import org.mockito.Mockito.`when` +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean + +@SpringBootTest(classes = [GetAgentsCrewByMissionId::class]) +class GetAgentsCrewByMissionIdTest { + + @Autowired + private lateinit var getAgentsCrewByMissionId: GetAgentsCrewByMissionId + + @MockBean + private lateinit var agentCrewRepository: IMissionCrewRepository + + + @Test + fun `execute should return sorted list of crew members by role priority`() { + + val missionId = 1 + + val johnDoe = AgentModel( + firstName = "John", + lastName = "Doe", + id = 1 + ) + + val janeDoe = AgentModel( + firstName = "Jane", + lastName = "Doe", + id = 2 + ) + + val alfredDeMusset = AgentModel( + firstName = "Alfred", + lastName = "de Musset", + id = 3 + ) + + val guyDeMaupassant = AgentModel( + firstName = "Guy", + lastName = "de Maupassant", + id = 4 + ) + + val chefMecano = AgentRoleModel( + title = "Chef mécanicien", + id = 1 + ) + + val secondCapitaine = AgentRoleModel( + title = "Second capitaine", + id = 2 + ) + + val cuisinier = AgentRoleModel( + title = "Cuisinier", + id = 3 + ) + + val commandant = AgentRoleModel( + title = "Commandant", + id = 4 + ) + + val crewMembers = listOf( + MissionCrewModel(role = chefMecano, agent = janeDoe, missionId = missionId, id = 1), + MissionCrewModel(role = secondCapitaine, agent = johnDoe, missionId = missionId, id = 2), + MissionCrewModel(role = cuisinier, agent = alfredDeMusset, missionId = missionId, id = 3), + MissionCrewModel(role = commandant, agent = guyDeMaupassant, missionId = missionId, id = 4), + ) + + `when`(agentCrewRepository.findByMissionId(missionId)).thenReturn(crewMembers) + + val sortedCrew = getAgentsCrewByMissionId.execute(missionId, commentDefaultsToString = false) + + // Assert + val expectedRoles = listOf("Commandant", "Second capitaine", "Chef mécanicien", "Cuisinier") + assertEquals(expectedRoles, sortedCrew.map { it.role.title }) + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1d334cd6..01ccb78b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "2.2.5", + "version": "2.2.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "2.2.5", + "version": "2.2.6", "dependencies": { "@apollo/client": "^3.11.0", "@mtes-mct/monitor-ui": "^14.3.1", diff --git a/frontend/package.json b/frontend/package.json index d9f78def..0498ea18 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "2.2.5", + "version": "2.2.6", "private": true, "type": "module", "engines": { diff --git a/frontend/src/features/pam/mission/components/elements/actions/action-illegal-immigration-form.test.tsx b/frontend/src/features/pam/mission/components/elements/actions/action-illegal-immigration-form.test.tsx index ba3e7284..8ae7542b 100644 --- a/frontend/src/features/pam/mission/components/elements/actions/action-illegal-immigration-form.test.tsx +++ b/frontend/src/features/pam/mission/components/elements/actions/action-illegal-immigration-form.test.tsx @@ -7,6 +7,7 @@ import * as useActionByIdModule from '@features/pam/mission/hooks/use-action-by- import * as useIsMissionFinishedModule from '@features/pam/mission/hooks/use-is-mission-finished.tsx' import { vi } from 'vitest' import { render } from '../../../../../../test-utils.tsx' +import { THEME } from '@mtes-mct/monitor-ui' vi.mock('react-router-dom', async importOriginal => { const actual = await importOriginal() @@ -50,9 +51,10 @@ describe('ActionIllegalImmigrationForm', () => { }) it('should render error when no values and mission is finished', async () => { - const red = '#e1000f' vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(true) const wrapper = render() - expect(wrapper.getByRole('nbOfInterceptedVessels')).toHaveStyle(`border: 1px solid ${red};`) + expect(wrapper.getByRole('nbOfInterceptedVessels')).toHaveStyle( + `border: 1px solid ${THEME.color.maximumRed.toLowerCase()};` + ) }) }) diff --git a/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.test.tsx b/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.test.tsx index b0547fd3..a39589bf 100644 --- a/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.test.tsx +++ b/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.test.tsx @@ -4,6 +4,8 @@ import { vi } from 'vitest' import { fireEvent, render, screen } from '../../../../../../test-utils.tsx' import UIThemeWrapper from '../../../../../common/components/ui/ui-theme-wrapper.tsx' import MissionRecognizedVessel from './mission-recognized-vessel.tsx' +import * as useIsMissionFinishedModule from '@features/pam/mission/hooks/use-is-mission-finished.tsx' +import { THEME } from '@mtes-mct/monitor-ui' const info = { id: 3, @@ -11,7 +13,7 @@ const info = { consumedGOInLiters: 0, consumedFuelInLiters: 0, serviceId: 5, - nbrOfRecognizedVessel: 0 + nbrOfRecognizedVessel: 5 } as MissionGeneralInfo const mutateMock = vi.fn() @@ -35,23 +37,52 @@ describe('MissionRecognizedVessel', () => { expect(screen.getByText(`Nombre total de navires reconnus dans les approches maritimes (ZEE)`)).toBeTruthy() }) - it('should call update information general', () => { - const nbrOfRecognizedVessel = 9 - vi.useFakeTimers({ shouldAdvanceTime: true }) - const wrapper = render( - - - - ) - const element = wrapper.getByTestId('mission-information-general-recognized-vessel') - fireEvent.change(element, { - target: { value: nbrOfRecognizedVessel } + describe('Validation', () => { + it('should show error validation when mission is finished but no info ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(true) + render() + const element = screen.getByLabelText('Nombre total de navires reconnus dans les approches maritimes (ZEE)') + expect(getComputedStyle(element).borderColor).toBe(THEME.color.maximumRed.toLowerCase()) }) - expect(mutateMock).not.toHaveBeenCalled() - vi.advanceTimersByTime(5000) - expect(mutateMock).toHaveBeenCalledTimes(1) - expect(mutateMock).toHaveBeenCalledWith({ - variables: { info: expect.objectContaining({ nbrOfRecognizedVessel }) } + it('should not show error validation when mission is finished but info ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(true) + render() + const element = screen.getByLabelText('Nombre total de navires reconnus dans les approches maritimes (ZEE)') + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) + }) + it('should not show error validation when mission is not finished but info ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(false) + render() + const element = screen.getByLabelText('Nombre total de navires reconnus dans les approches maritimes (ZEE)') + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) + }) + it('should not show error validation when mission is not finished and no info ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(false) + render() + const element = screen.getByLabelText('Nombre total de navires reconnus dans les approches maritimes (ZEE)') + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) + }) + }) + + describe('Updating data', () => { + it('should call update information general on change', () => { + const nbrOfRecognizedVessel = 9 + vi.useFakeTimers({ shouldAdvanceTime: true }) + const wrapper = render( + + + + ) + const element = wrapper.getByTestId('mission-information-general-recognized-vessel') + fireEvent.change(element, { + target: { value: nbrOfRecognizedVessel } + }) + expect(mutateMock).not.toHaveBeenCalled() + vi.advanceTimersByTime(5000) + expect(mutateMock).toHaveBeenCalledTimes(1) + expect(mutateMock).toHaveBeenCalledWith({ + variables: { info: expect.objectContaining({ nbrOfRecognizedVessel }) } + }) }) }) }) diff --git a/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.tsx b/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.tsx index 24aedc23..03f3a904 100644 --- a/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.tsx +++ b/frontend/src/features/pam/mission/components/elements/general-info/mission-recognized-vessel.tsx @@ -3,6 +3,7 @@ import { FormikEffect, FormikNumberInput } from '@mtes-mct/monitor-ui' import { Formik } from 'formik' import React, { useEffect, useRef, useState } from 'react' import useAddOrUpdateGeneralInfo from '../../../hooks/use-add-update-distance-consumption.tsx' +import useIsMissionFinished from '@features/pam/mission/hooks/use-is-mission-finished.tsx' const DEBOUNCE_TIME_TRIGGER = 1000 @@ -17,6 +18,8 @@ interface MissionRecognizedVesselProps { const MissionRecognizedVessel: React.FC = ({ missionId, generalInfo }) => { const [updateGeneralInfo] = useAddOrUpdateGeneralInfo() + const isMissionFinished = useIsMissionFinished(missionId?.toString()) + const timerRef = useRef>() const [initValue, setInitValue] = useState() @@ -30,17 +33,27 @@ const MissionRecognizedVessel: React.FC = ({ missi } const updateRecognizedVessel = async (nbrOfRecognizedVessel?: number) => { - if (nbrOfRecognizedVessel === undefined || nbrOfRecognizedVessel === generalInfo?.nbrOfRecognizedVessel) return const info = { ...generalInfo, missionId, nbrOfRecognizedVessel } await updateGeneralInfo({ variables: { info } }) } + const validateError = (isMissionFinished?: boolean, nbrOfRecognizedVessel?: number) => + isMissionFinished && !nbrOfRecognizedVessel + ? { nbrOfRecognizedVessel: 'Nombre total de navires reconnus dans les approches maritimes est requis' } + : undefined + return ( <> {initValue && ( - + validateError(isMissionFinished, values.nbrOfRecognizedVessel)} + > <> = ({ missi name="nbrOfRecognizedVessel" data-testid="mission-information-general-recognized-vessel" label="Nombre total de navires reconnus dans les approches maritimes (ZEE)" + isErrorMessageHidden={true} /> diff --git a/frontend/src/features/pam/mission/components/elements/mission-observation-unit.test.tsx b/frontend/src/features/pam/mission/components/elements/mission-observation-unit.test.tsx index b78d3aaf..311cdf62 100644 --- a/frontend/src/features/pam/mission/components/elements/mission-observation-unit.test.tsx +++ b/frontend/src/features/pam/mission/components/elements/mission-observation-unit.test.tsx @@ -2,6 +2,8 @@ import * as usePatchModule from '@features/pam/mission/hooks/use-patch-mission-e import { vi } from 'vitest' import { fireEvent, render, screen, waitFor } from '../../../../../test-utils.tsx' import MissionObservationByUnit from './mission-observations-unit.tsx' +import { THEME } from '@mtes-mct/monitor-ui' +import * as useIsMissionFinishedModule from '@features/pam/mission/hooks/use-is-mission-finished.tsx' const patchMock = vi.fn() @@ -17,48 +19,78 @@ describe('MissionObservation', () => { it('should render observation', () => { render() expect(patchMock).not.toHaveBeenCalled() - expect(screen.getByText(`Observation générale à l'échelle de la mission (remarques, résumé)`)).toBeInTheDocument() + const element = screen.getByLabelText("Observation générale à l'échelle de la mission (remarques, résumé)") + expect(element).toBeInTheDocument() expect(screen.getByText('My beautiful observation')).toBeInTheDocument() }) - it('should call update observation', () => { - const wrapper = render() - const element = wrapper.getByTestId('mission-general-observation') - fireEvent.change(element, { - target: { value: 'my new observations' } + describe('Validation', () => { + it('should show error validation when mission is finished but no observations ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(true) + render() + const element = screen.getByLabelText("Observation générale à l'échelle de la mission (remarques, résumé)") + expect(getComputedStyle(element).borderColor).toBe(THEME.color.maximumRed.toLowerCase()) }) - waitFor(() => { - expect(patchMock).toHaveBeenCalled() + it('should not show error validation when mission is finished but observations ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(true) + render() + const element = screen.getByLabelText("Observation générale à l'échelle de la mission (remarques, résumé)") + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) }) - }) - - it('should trigger 5 secondes after typing', () => { - vi.useFakeTimers({ shouldAdvanceTime: true }) - const observationsByUnit = 'my observations!!!!!' - const wrapper = render() - const element = wrapper.getByTestId('mission-general-observation') - fireEvent.change(element, { - target: { value: observationsByUnit } + it('should not show error validation when mission is not finished but observations ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(false) + render() + const element = screen.getByLabelText("Observation générale à l'échelle de la mission (remarques, résumé)") + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) }) - expect(patchMock).not.toHaveBeenCalled() - vi.advanceTimersByTime(5000) - expect(patchMock).toHaveBeenCalled() - expect(patchMock).toHaveBeenCalledWith({ - variables: { mission: expect.objectContaining({ observationsByUnit }) } + it('should not show error validation when mission is not finished and no observations ', () => { + vi.spyOn(useIsMissionFinishedModule, 'default').mockReturnValue(false) + render() + const element = screen.getByLabelText("Observation générale à l'échelle de la mission (remarques, résumé)") + expect(getComputedStyle(element).borderColor).not.toBe(THEME.color.maximumRed.toLowerCase()) }) }) - it('should call update observations event empty', async () => { - vi.useFakeTimers({ shouldAdvanceTime: true }) - const wrapper = render() - const element = wrapper.getByTestId('mission-general-observation') - fireEvent.change(element, { - target: { value: '' } + describe('Updating the data', () => { + it('should call update observation on change event', () => { + const wrapper = render() + const element = wrapper.getByTestId('mission-general-observation') + fireEvent.change(element, { + target: { value: 'my new observations' } + }) + waitFor(() => { + expect(patchMock).toHaveBeenCalled() + }) }) - vi.advanceTimersByTime(5000) - expect(patchMock).toHaveBeenCalled() - expect(patchMock).toHaveBeenCalledWith({ - variables: { mission: expect.objectContaining({ observationsByUnit: '' }) } + + it('should trigger 5 secondes after typing', () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const observationsByUnit = 'my observations!!!!!' + const wrapper = render() + const element = wrapper.getByTestId('mission-general-observation') + fireEvent.change(element, { + target: { value: observationsByUnit } + }) + expect(patchMock).not.toHaveBeenCalled() + vi.advanceTimersByTime(5000) + expect(patchMock).toHaveBeenCalled() + expect(patchMock).toHaveBeenCalledWith({ + variables: { mission: expect.objectContaining({ observationsByUnit }) } + }) + }) + + it('should call update observations event empty', async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const wrapper = render() + const element = wrapper.getByTestId('mission-general-observation') + fireEvent.change(element, { + target: { value: '' } + }) + vi.advanceTimersByTime(5000) + expect(patchMock).toHaveBeenCalled() + expect(patchMock).toHaveBeenCalledWith({ + variables: { mission: expect.objectContaining({ observationsByUnit: '' }) } + }) }) }) }) diff --git a/frontend/src/features/pam/mission/components/elements/mission-observations-unit.tsx b/frontend/src/features/pam/mission/components/elements/mission-observations-unit.tsx index 1f298a2e..bedcbcb9 100644 --- a/frontend/src/features/pam/mission/components/elements/mission-observations-unit.tsx +++ b/frontend/src/features/pam/mission/components/elements/mission-observations-unit.tsx @@ -2,6 +2,7 @@ import { FormikEffect, FormikTextarea } from '@mtes-mct/monitor-ui' import { Formik } from 'formik' import React, { useEffect, useRef, useState } from 'react' import usePatchMissionEnv from '../../hooks/use-patch-mission-env.tsx' +import useIsMissionFinished from '@features/pam/mission/hooks/use-is-mission-finished.tsx' const DEBOUNCE_TIME_TRIGGER = 5000 @@ -16,6 +17,8 @@ interface MissionObservationsByUnitProps { const MissionObservationsUnit: React.FC = ({ missionId, observationsByUnit }) => { const [patchMissionObservation] = usePatchMissionEnv() + const isMissionFinished = useIsMissionFinished(missionId?.toString()) + const timerRef = useRef>() const [initValue, setInitValue] = useState() @@ -40,10 +43,21 @@ const MissionObservationsUnit: React.FC = ({ mis }) } + const validateError = (isMissionFinished?: boolean, observations?: string) => + isMissionFinished && !observations + ? { observations: "L'observation générale de la mission est requise" } + : undefined + return ( <> {initValue && ( - + validateError(isMissionFinished, values.observations)} + > <> = ({ mis name="observations" data-testid="mission-general-observation" label="Observation générale à l'échelle de la mission (remarques, résumé)" + isErrorMessageHidden={true} />