Skip to content

Commit

Permalink
Fix few e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
louptheron committed Jan 25, 2024
1 parent e92e429 commit d45dc5a
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 55 deletions.
37 changes: 26 additions & 11 deletions frontend/cypress/e2e/side_window/mission_form/main_form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ context('Side Window > Mission Form > Main Form', () => {
statusCode: 201
}).as('createMission')

cy.intercept('POST', '/api/v1/missions/1', {
body: {
id: 1
},
statusCode: 201
}).as('updateMission')

cy.fill('Début de mission', [2023, 2, 1, 12, 34])
cy.fill('Fin de mission', [2023, 2, 1, 13, 45])

Expand All @@ -168,6 +175,7 @@ context('Side Window > Mission Form > Main Form', () => {

cy.fill('Administration 1', 'DDTM')
cy.fill('Unité 1', 'Cultures marines – DDTM 40')
cy.wait(500)
cy.fill('Moyen 1', ['Semi-rigide 1'])
cy.fill('Contact de l’unité 1', 'Bob')
cy.fill('Contact de l’unité 1', 'Bob')
Expand All @@ -178,6 +186,7 @@ context('Side Window > Mission Form > Main Form', () => {
cy.fill('Unité 2', 'DREAL Pays-de-La-Loire')
cy.fill('Moyen 2', ['ALTAIR', 'ARIOLA'])
cy.fill('Contact de l’unité 2', 'Bob 2')
cy.wait(500)

cy.fill('CACEM : orientations, observations', 'Une note.')
cy.fill('CNSP : orientations, observations', 'Une autre note.')
Expand All @@ -186,9 +195,8 @@ context('Side Window > Mission Form > Main Form', () => {

cy.wait(500)

// Approx. 4 requests are sent to the server
cy.waitForLastRequest(
'@createMission',
'@updateMission',
{
body: {
closedBy: 'Doris',
Expand Down Expand Up @@ -237,7 +245,7 @@ context('Side Window > Mission Form > Main Form', () => {
startDateTimeUtc: '2023-02-01T12:34:00.000Z'
}
},
5
10
)
.its('response.statusCode')
.should('eq', 201)
Expand Down Expand Up @@ -393,19 +401,24 @@ context('Side Window > Mission Form > Main Form', () => {
})

it('Should close a new mission', () => {
cy.intercept('POST', '/api/v1/missions/1', {
body: {
id: 1,
isClosed: true
},
statusCode: 201
}).as('updateMission')
openSideWindowNewMission()
fillSideWindowMissionFormBase(Mission.MissionTypeLabel.SEA, true)
fillSideWindowMissionFormBase(Mission.MissionTypeLabel.SEA, false)

cy.fill('Clôturé par', 'Doris')

cy.wait(300)
cy.wait(500)

cy.clickButton('Clôturer')

cy.wait(250)

cy.waitForLastRequest(
'@createMission',
'@updateMission',
{
body: {
// We check this prop to be sure all the data is there (this is the last field to be filled)
Expand All @@ -414,7 +427,7 @@ context('Side Window > Mission Form > Main Form', () => {
isGeometryComputedFromControls: false
}
},
5
10
)
.its('response.statusCode')
.should('eq', 201)
Expand Down Expand Up @@ -761,7 +774,7 @@ context('Side Window > Mission Form > Main Form', () => {
.its('mockEventSources' as any)
.then(mockEventSources => {
// URL sur la CI : http://0.0.0.0:8081/api/v1/missions/sse'
// URL en local : /api/v1/missions/sse
// URL en local : //localhost:8081/api/v1/missions/sse
mockEventSources['http://0.0.0.0:8081/api/v1/missions/sse'].emitOpen()
mockEventSources['http://0.0.0.0:8081/api/v1/missions/sse'].emit(
'MISSION_UPDATE',
Expand Down Expand Up @@ -824,12 +837,13 @@ context('Side Window > Mission Form > Main Form', () => {
body: [],
statusCode: 200
})
cy.wait(500)

cy.window()
.its('mockEventSources' as any)
.then(mockEventSources => {
// URL sur la CI : http://0.0.0.0:8081/api/v1/missions/sse
// URL en local : /api/v1/missions/sse
// URL en local : //localhost:8081/api/v1/missions/sse
mockEventSources['http://0.0.0.0:8081/api/v1/missions/sse'].emitOpen()
mockEventSources['http://0.0.0.0:8081/api/v1/missions/sse'].emit(
'MISSION_UPDATE',
Expand Down Expand Up @@ -880,6 +894,7 @@ context('Side Window > Mission Form > Main Form', () => {
})
)
})
cy.wait(250)

cy.fill('CNSP : orientations, observations', 'Une autre note (dummy updtae to send a request).')

Expand Down
2 changes: 1 addition & 1 deletion frontend/puppeteer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ services:

flyway:
image: flyway/flyway
command: migrate -password=postgres -schemas=public -user=postgres -url=jdbc:postgresql://db:5432/monitorfishdb
command: migrate -password=postgres -schemas=public -user=postgres -url=jdbc:postgresql://monitorfish-db:5432/monitorfishdb
volumes:
- ../../backend/src/main/resources/db/:/flyway/sql
depends_on:
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/domain/entities/controlUnits/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export function getControlUnitsOptionsFromControlUnits(
controlUnits: LegacyControlUnit.LegacyControlUnit[] | undefined = [],
selectedAdministrations?: string[]
): {
activeControlUnits: LegacyControlUnit.LegacyControlUnit[]
administrationsAsOptions: Option[]
unitsAsOptions: Option[]
} {
Expand All @@ -28,7 +27,6 @@ export function getControlUnitsOptionsFromControlUnits(
const unitsAsOptions = getOptionsFromStrings(uniqueSortedUnits)

return {
activeControlUnits,
administrationsAsOptions,
unitsAsOptions
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/domain/entities/mission/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export namespace Mission {
isClosed: boolean
isGeometryComputedFromControls: boolean
isUnderJdp?: boolean | undefined
isValid: boolean
// For internal Formik validation purpose
isValid?: boolean | undefined
missionSource: MissionSource
missionTypes: MissionType[]
observationsCacem?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ import {
useNewWindow
} from '@mtes-mct/monitor-ui'
import { useField } from 'formik'
import { useCallback, useMemo, useState } from 'react'
import { uniqBy } from 'lodash'
import { useCallback, useMemo } from 'react'
import styled from 'styled-components'

import {
findControlUnitByname,
mapControlUnitsToUniqueSortedNamesAsOptions,
mapControlUnitToSortedResourcesAsOptions
mapToSortedResourcesAsOptions
} from './utils'
import { FIVE_MINUTES } from '../../../../../api/APIWorker'
import { Mission } from '../../../../../domain/entities/mission/types'
import { useMainAppSelector } from '../../../../../hooks/useMainAppSelector'
import { isNotArchived } from '../../../../../utils/isNotArchived'
import { useGetEngagedControlUnitsQuery } from '../../apis'
import { INITIAL_MISSION_CONTROL_UNIT } from '../../constants'
import { isValidControlUnit } from '../../utils'

import type { LegacyControlUnit } from '../../../../../domain/types/legacyControlUnit'
import type { MissionMainFormValues } from '../../types'
Expand Down Expand Up @@ -71,8 +72,10 @@ export function ControlUnitSelect({
return engagedControlUnitsData
}, [engagedControlUnitsData])

const [selectedControlUnit, setSelectedControlUnit] = useState<LegacyControlUnit.LegacyControlUnit | undefined>(
isValidControlUnit(value) ? value : undefined
// Include archived control units (and administrations) if they're already selected
const activeAndSelectedControlUnits = useMemo(
() => allControlUnits.filter(controlUnit => isNotArchived(controlUnit) || value.name === controlUnit.name) || [],
[allControlUnits, value]
)

const engagedControlUnit = engagedControlUnits.find(engaged => engaged.controlUnit.id === value.id)
Expand All @@ -91,10 +94,25 @@ export function ControlUnitSelect({
return mapControlUnitsToUniqueSortedNamesAsOptions(selectedAdministrationControlUnits)
}, [allControlUnits, allNamesAsOptions, value])

const selectedControlUnitResourcesAsOptions = useMemo(
// Include archived resources if they're already selected
const activeWithSelectedControlUnitResources = useMemo(() => {
const activeControlUnitResources =
activeAndSelectedControlUnits.find(unit => unit.administration === value.administration && unit.id === value.id)
?.resources || []
// TODO Remove LegacyControlUnitResource to filter archived resources :
// .filter(isNotArchived)

const resources = [...activeControlUnitResources, ...value.resources]

return uniqBy(resources, 'id')
}, [activeAndSelectedControlUnits, value])

const controlUnitResourcesAsOptions = useMemo(
(): Option<LegacyControlUnit.LegacyControlUnitResource>[] =>
selectedControlUnit ? mapControlUnitToSortedResourcesAsOptions(selectedControlUnit) : [],
[selectedControlUnit]
activeWithSelectedControlUnitResources
? mapToSortedResourcesAsOptions(activeWithSelectedControlUnitResources)
: [],
[activeWithSelectedControlUnitResources]
)

const handleAdministrationChange = useCallback(
Expand All @@ -104,8 +122,6 @@ export function ControlUnitSelect({
administration: nextAdministration
}

setSelectedControlUnit(undefined)

onChange(index, nextControlUnit)
},
[index, onChange]
Expand All @@ -130,8 +146,6 @@ export function ControlUnitSelect({
administration: value.administration
}

setSelectedControlUnit(nextSelectedControlUnit)

onChange(index, nextControlUnit)
},
[allControlUnits, value, index, isLoading, onChange]
Expand Down Expand Up @@ -221,7 +235,7 @@ export function ControlUnitSelect({
label={`Moyen ${index + 1}`}
name={`resources_${index}`}
onChange={handleResourcesChange}
options={selectedControlUnitResourcesAsOptions}
options={controlUnitResourcesAsOptions}
optionValueKey="name"
value={value.resources}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ export function FormikMultiControlUnitPicker({ name }: FormikMultiControlUnitPic

const controlUnitsQuery = useGetLegacyControlUnitsQuery(undefined)

const {
activeControlUnits: allActiveControlUnits,
administrationsAsOptions: allAdministrationsAsOptions,
unitsAsOptions: allNamesAsOptions
} = useMemo(() => getControlUnitsOptionsFromControlUnits(controlUnitsQuery.data), [controlUnitsQuery.data])
const { administrationsAsOptions: allAdministrationsAsOptions, unitsAsOptions: allNamesAsOptions } = useMemo(
() => getControlUnitsOptionsFromControlUnits(controlUnitsQuery.data),
[controlUnitsQuery.data]
)

const errors = (allErrors[name] || []) as Array<{
administration: string
Expand Down Expand Up @@ -83,7 +82,7 @@ export function FormikMultiControlUnitPicker({ name }: FormikMultiControlUnitPic
// eslint-disable-next-line react/no-array-index-key
key={`unit${index}`}
allAdministrationsAsOptions={allAdministrationsAsOptions}
allControlUnits={allActiveControlUnits}
allControlUnits={controlUnitsQuery.data || []}
allNamesAsOptions={allNamesAsOptions}
error={errors[index]}
index={index}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export function mapControlUnitsToUniqueSortedNamesAsOptions(
return uniqueSortedNamesAsOptions
}

export function mapControlUnitToSortedResourcesAsOptions(
controlUnit: LegacyControlUnit.LegacyControlUnit
export function mapToSortedResourcesAsOptions(
resources: LegacyControlUnit.LegacyControlUnitResource[]
): Array<Option<LegacyControlUnit.LegacyControlUnitResource>> {
const sortedResources = sortBy(controlUnit.resources, ({ name }) => name)
const sortedResources = sortBy(resources, ({ name }) => name)
const sortedResourcesAsOptions = sortedResources.map(sortedResource => ({
label: sortedResource.name,
value: sortedResource
Expand Down
27 changes: 12 additions & 15 deletions frontend/src/features/SideWindow/MissionForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ import type { MissionAction } from '../../../domain/types/missionAction'

export function MissionForm() {
const dispatch = useMainAppDispatch()
const editedMissionId = useMainAppSelector(store => store.sideWindow.selectedPath.id)
const missionIdFromPath = useMainAppSelector(store => store.sideWindow.selectedPath.id)
const newMissionId = useRef<number | undefined>(undefined)
const missionId = editedMissionId || newMissionId?.current
const missionId = missionIdFromPath || newMissionId?.current
const isDraftCancellationConfirmationDialogOpen = useMainAppSelector(
store => store.sideWindow.isDraftCancellationConfirmationDialogOpen
)
Expand Down Expand Up @@ -94,14 +94,14 @@ export function MissionForm() {
isUpdatingMissionAction

const isLoading =
editedMissionId && ((isFetchingMission && !missionData) || (isFetchingMissionActions && !missionActionsData))
missionIdFromPath && ((isFetchingMission && !missionData) || (isFetchingMissionActions && !missionActionsData))

const [mainFormValues, setMainFormValues] = useState<MissionMainFormValues | undefined>(undefined)
const [actionsFormValues, setActionsFormValues] = useState<MissionActionFormValues[]>([])
const [editedActionIndex, setEditedActionIndex] = useState<number | undefined>(undefined)
const [isDeletionConfirmationDialogOpen, setIsDeletionConfirmationDialogOpen] = useState(false)
const [title, setTitle] = useState(getTitleFromMissionMainFormValues(undefined, undefined))
const previousMissionId = usePrevious(editedMissionId)
const previousMissionId = usePrevious(missionIdFromPath)

// We use these keys to fully control when to re-render `<MainForm />` & `<ActionForm />`
// since they are fully memoized in order to optimize their (heavy) re-rendering
Expand Down Expand Up @@ -203,7 +203,7 @@ export function MissionForm() {
dispatch(missionActions.setIsListeningToEvents(false))
let nextMissionId: number

if (!editedMissionId) {
if (!missionId) {
const newMission = getMissionDataFromMissionFormValues(createdOrUpdatedMainFormValues)
// TODO Override Redux RTK typings globally.
// Redux RTK typing is wrong, this should be a tuple-like to help TS discriminate `data` from `error`.
Expand All @@ -221,10 +221,7 @@ export function MissionForm() {
updatedAtUtc: data.updatedAtUtc
})
} else {
const updatedMission = getUpdatedMissionFromMissionMainFormValues(
editedMissionId,
createdOrUpdatedMainFormValues
)
const updatedMission = getUpdatedMissionFromMissionMainFormValues(missionId, createdOrUpdatedMainFormValues)
const { data, error } = (await updateMission(updatedMission)) as any
if (!data) {
throw new FrontendError('`updateMission()` failed', error)
Expand All @@ -234,7 +231,7 @@ export function MissionForm() {
...createdOrUpdatedMainFormValues,
updatedAtUtc: data.updatedAtUtc
})
nextMissionId = editedMissionId
nextMissionId = missionId
}

const { deletedMissionActionIds, updatedMissionActionDatas } =
Expand Down Expand Up @@ -286,7 +283,7 @@ export function MissionForm() {
deleteMissionAction,
updateMission,
updateMissionAction,
editedMissionId
missionId
]
)

Expand All @@ -295,13 +292,13 @@ export function MissionForm() {
}, [dispatch])

const handleDelete = useCallback(async () => {
if (!editedMissionId) {
if (!missionIdFromPath) {
throw new FrontendError('`missionId` is undefined')
}

await deleteMission(editedMissionId)
await deleteMission(missionIdFromPath)
dispatch(sideWindowActions.openOrFocusAndGoTo({ menu: SideWindowMenuKey.MISSION_LIST }))
}, [deleteMission, dispatch, editedMissionId])
}, [deleteMission, dispatch, missionIdFromPath])

const reopen = useCallback(() => {
if (!mainFormValues) {
Expand Down Expand Up @@ -675,7 +672,7 @@ export function MissionForm() {
</Body>
<Footer>
<div>
{editedMissionId && (
{missionIdFromPath && (
<Button
accent={Accent.SECONDARY}
disabled={isLoading || isSaving || mainFormValues?.missionSource !== Mission.MissionSource.MONITORFISH}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/SideWindow/MissionForm/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function getMissionDataFromMissionFormValues(mainFormValues: MissionMainF
throw new FormError(mainFormValues, 'startDateTimeUtc', FormErrorCode.MISSING_OR_UNDEFINED)
}

const missionBaseValues = omit(['controlUnits'], mainFormValues)
const missionBaseValues = omit(['controlUnits', 'isValid'], mainFormValues)

const validControlUnits = mainFormValues.controlUnits.map(getValidMissionDataControlUnit)
const missionTypes = mainFormValues.missionTypes || []
Expand Down
Loading

0 comments on commit d45dc5a

Please sign in to comment.