Skip to content

Commit

Permalink
[Hotfix] Fixe la sélection de période sur la liste des unités (#918)
Browse files Browse the repository at this point in the history
## Related Pull Requests & Issues

None

----

- [x] Tests E2E (Cypress)
  • Loading branch information
ivangabriele authored Oct 26, 2023
2 parents b2d1126 + d9d5c3c commit 3c255ed
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 37 deletions.
145 changes: 145 additions & 0 deletions frontend/cypress/e2e/side_window/mission_list/filters.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { customDayjs } from '../../utils/customDayjs'
import { getUtcDateInMultipleFormats } from '../../utils/getUtcDateInMultipleFormats'

context('Side Window > Mission List > Filter Bar', () => {
beforeEach(() => {
cy.viewport(1280, 1024)

cy.visit(`/side_window`).wait(1000)
})

it('Should filter missions for the current day', () => {
const currentDay = encodeURIComponent(customDayjs().utc().startOf('day').toISOString())
cy.intercept('GET', `/bff/v1/missions?&startedAfterDateTime=${currentDay}*`).as('getMissions')

cy.fill('Période', 'Aujourd’hui')
cy.wait('@getMissions')

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
})

it('Should filter missions for the current month', () => {
const currentDay = encodeURIComponent(customDayjs().utc().subtract(1, 'month').startOf('day').toISOString())
cy.intercept('GET', `/bff/v1/missions?&startedAfterDateTime=${currentDay}*`).as('getMissions')

cy.fill('Période', 'Un mois')
cy.wait('@getMissions')

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
})

it('Should filter missions for the custom date range', () => {
const expectedStartDate = getUtcDateInMultipleFormats('2023-05-01T00:00:00.000Z')
const expectedEndDate = getUtcDateInMultipleFormats('2023-05-31T23:59:59.000Z')
cy.intercept(
'GET',
`/bff/v1/missions?&startedAfterDateTime=${expectedStartDate.utcDateAsEncodedString}&startedBeforeDateTime=${expectedEndDate.utcDateAsEncodedString}*`
).as('getMissions')

cy.fill('Période', 'Période spécifique')
cy.fill('Période spécifique', [expectedStartDate.utcDateTuple, expectedEndDate.utcDateTuple])
cy.wait('@getMissions')
})

it('Should filter missions by source', () => {
cy.intercept('GET', `*missionSource=MONITORENV*`).as('getMissions')
cy.fill('Origine', 'CACEM')
cy.wait('@getMissions')

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'CACEM')
})
})

it('Should filter missions by administrations', () => {
cy.getDataCy('select-administration-filter').click().wait(100)
cy.get('.rs-picker-search-bar-input').type('DDTM').wait(100)
cy.get('[data-key="DDTM"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'DDTM')
})
})

it('Should filter missions by units', () => {
cy.getDataCy('select-units-filter').click().wait(100)
cy.get('.rs-picker-search-bar-input').type('BSN').wait(100)
cy.get('[data-key="10015"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'BSN Ste Maxime')
})
})

it('Should filter missions by types', () => {
cy.getDataCy('select-types-filter').click().wait(100)
cy.get('[data-key="SEA"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'Mer')
})
})

it('Should filter missions by sea fronts', () => {
cy.getDataCy('select-seaFronts-filter').click().wait(100)
cy.get('[data-key="MED"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'MED')
})
})

it('Should filter missions by statuses', () => {
cy.getDataCy('select-statuses-filter').click().wait(100)
cy.get('[data-key="PENDING"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'En cours')
})
})

it('Should filter missions by themes', () => {
cy.getDataCy('select-theme-filter').click().wait(100)
cy.get('.rs-picker-search-bar-input').type('Police').wait(100)
cy.get('[data-key="Police des activités de cultures marines"]').click().wait(100).clickOutside()

cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
if (index === 0) {
return
}

cy.wrap(row).should('contain', 'Police des activités de cultures marines')
})
})
})
22 changes: 22 additions & 0 deletions frontend/cypress/e2e/utils/customDayjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import dayjs from 'dayjs'
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
import localeData from 'dayjs/plugin/localeData'
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import timezone from 'dayjs/plugin/timezone'
import updateLocale from 'dayjs/plugin/updateLocale'
import utc from 'dayjs/plugin/utc'

import 'dayjs/locale/fr'

dayjs.extend(isSameOrAfter)
dayjs.extend(isSameOrBefore)
dayjs.extend(localeData)
dayjs.extend(quarterOfYear)
dayjs.extend(timezone)
dayjs.extend(updateLocale)
dayjs.extend(utc)

dayjs.locale('fr')

export { dayjs as customDayjs }
5 changes: 5 additions & 0 deletions frontend/cypress/e2e/utils/expectPathToBe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function expectPathToBe(expectedPath: string) {
cy.location().should(location => {
assert.equal(location.pathname, expectedPath)
})
}
7 changes: 7 additions & 0 deletions frontend/cypress/e2e/utils/getLastIdFromCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { sortBy } from 'lodash/fp'

import type { CollectionItem } from '@mtes-mct/monitor-ui'

export function getLastIdFromCollection(collection: CollectionItem[]) {
return (sortBy('id', collection)[collection.length - 1] as CollectionItem).id
}
30 changes: 30 additions & 0 deletions frontend/cypress/e2e/utils/getUtcDateInMultipleFormats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { customDayjs } from './customDayjs'

export function getUtcDateInMultipleFormats(date?: string) {
const utcDateAsDayjs = customDayjs(date).utc()

return {
utcDateAsDayjs,
utcDateAsEncodedString: encodeURIComponent(utcDateAsDayjs.toISOString()),
/**
* ISO string without seconds, milliseconds, and timezone offset.
*
* @example
* `2023-06-08T13:54`
*/
utcDateAsShortString: utcDateAsDayjs.toISOString().substring(0, 16),
utcDateAsString: utcDateAsDayjs.toISOString(),
utcDateTuple: [utcDateAsDayjs.year(), utcDateAsDayjs.month() + 1, utcDateAsDayjs.date()] as [
number,
number,
number
],
utcDateTupleWithTime: [
utcDateAsDayjs.year(),
utcDateAsDayjs.month() + 1,
utcDateAsDayjs.date(),
utcDateAsDayjs.hour(),
utcDateAsDayjs.minute()
] as [number, number, number, number, number]
}
}
20 changes: 13 additions & 7 deletions frontend/src/domain/entities/dateRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,31 @@ export enum DateRangeEnum {
WEEK = 'WEEK'
}

export const dateRangeLabels = {
export type DateRangeLabel = {
[K in DateRangeEnum]: {
label: string
value: K
}
}
export const DATE_RANGE_LABEL: DateRangeLabel = {
/* eslint-disable sort-keys-fix/sort-keys-fix */
DAY: {
label: 'Aujourd’hui',
value: 'DAY'
value: DateRangeEnum.DAY
},
WEEK: {
label: 'Une semaine',
value: 'WEEK'
value: DateRangeEnum.WEEK
},
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
MONTH: {
label: 'Un mois',
value: 'MONTH'
value: DateRangeEnum.MONTH
},
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
CUSTOM: {
label: 'Période spécifique',
value: 'CUSTOM'
value: DateRangeEnum.CUSTOM
}
/* eslint-enable sort-keys-fix/sort-keys-fix */
}

export enum ReportingDateRangeEnum {
Expand Down
34 changes: 24 additions & 10 deletions frontend/src/domain/shared_slices/MissionFilters.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { customDayjs as dayjs } from '@mtes-mct/monitor-ui'
import { createSlice } from '@reduxjs/toolkit'
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

import { dateRangeLabels } from '../entities/dateRange'
import { DATE_RANGE_LABEL } from '../entities/dateRange'

export const SEVEN_DAYS_AGO = dayjs().subtract(7, 'days').toISOString()

export enum MissionFiltersEnum {
ADMINISTRATION_FILTER = 'selectedAdministrationNames',
PERIOD_FILTER = 'selectedPeriodFilter',
PERIOD_FILTER = 'selectedPeriod',
SEA_FRONT_FILTER = 'selectedSeaFronts',
SOURCE_FILTER = 'selectedMissionSource',
STARTED_AFTER_FILTER = 'startedAfter',
Expand All @@ -20,7 +20,7 @@ export enum MissionFiltersEnum {
UNIT_FILTER = 'selectedControlUnitIds'
}

type MissionFiltersSliceType = {
type MissionFilterValues = {
hasFilters: boolean
selectedAdministrationNames: string[]
selectedControlUnitIds: number[]
Expand All @@ -34,13 +34,19 @@ type MissionFiltersSliceType = {
startedBefore?: string
}

const initialState: MissionFiltersSliceType = {
export type MissionFiltersState = {
[K in MissionFiltersEnum]: MissionFilterValues[K]
} & {
hasFilters: boolean
}

const INITIAL_STATE: MissionFiltersState = {
hasFilters: false,
selectedAdministrationNames: [],
selectedControlUnitIds: [],
selectedMissionSource: undefined,
selectedMissionTypes: [],
selectedPeriod: dateRangeLabels.WEEK.value,
selectedPeriod: DATE_RANGE_LABEL.WEEK.value,
selectedSeaFronts: [],
selectedStatuses: [],
selectedThemes: [],
Expand All @@ -54,20 +60,28 @@ const persistConfig = {
}

const missionFiltersSlice = createSlice({
initialState,
initialState: INITIAL_STATE,
name: 'missionFilters',
reducers: {
resetMissionFilters() {
return { ...initialState }
return { ...INITIAL_STATE }
},

updateFilters(state, action) {
updateFilters<K extends MissionFiltersEnum>(
// TODO There is not `MissionFiltersState` type inference in `createSlice()`.
// Investigate why (this should be automatic).
state: MissionFiltersState,
action: PayloadAction<{
key: K
value: MissionFiltersState[K]
}>
) {
return {
...state,
[action.payload.key]: action.payload.value,
hasFilters:
(action.payload.value && action.payload.value.length > 0) ||
state.selectedPeriod !== dateRangeLabels.WEEK.value ||
state.selectedPeriod !== DATE_RANGE_LABEL.WEEK.value ||
state.selectedAdministrationNames.length > 0 ||
state.selectedControlUnitIds.length > 0 ||
state.selectedMissionTypes.length > 0 ||
Expand Down
21 changes: 16 additions & 5 deletions frontend/src/features/missions/MissionsList/Filters/FilterTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import styled from 'styled-components'
import { RTK_DEFAULT_QUERY_OPTIONS } from '../../../../api/constants'
import { useGetControlUnitsQuery } from '../../../../api/controlUnitsAPI'
import { missionStatusLabels, missionTypeEnum } from '../../../../domain/entities/missions'
import { MissionFiltersEnum, updateFilters } from '../../../../domain/shared_slices/MissionFilters'
import {
MissionFiltersEnum,
updateFilters,
type MissionFiltersState
} from '../../../../domain/shared_slices/MissionFilters'
import { useAppDispatch } from '../../../../hooks/useAppDispatch'
import { useAppSelector } from '../../../../hooks/useAppSelector'
import { FrontendError } from '../../../../libs/FrontendError'

export function FilterTags() {
const dispatch = useAppDispatch()
Expand All @@ -21,12 +26,18 @@ export function FilterTags() {

const controlUnits = useGetControlUnitsQuery(undefined, RTK_DEFAULT_QUERY_OPTIONS)

const onDeleteTag = (
const onDeleteTag = <K extends MissionFiltersEnum>(
valueToDelete: number | string,
filterKey: MissionFiltersEnum,
selectedValues: Array<number | string>
filterKey: K,
selectedValues: MissionFiltersState[K]
) => {
const nextSelectedValues = selectedValues.filter(selectedValue => selectedValue !== valueToDelete)
if (!Array.isArray(selectedValues)) {
throw new FrontendError('`selectedValues` should be an array.')
}

const nextSelectedValues = selectedValues.filter(selectedValue => selectedValue !== valueToDelete) as
| string[]
| number[]
dispatch(updateFilters({ key: filterKey, value: nextSelectedValues }))
}

Expand Down
Loading

0 comments on commit 3c255ed

Please sign in to comment.