Skip to content

Commit

Permalink
feature: render release date when available
Browse files Browse the repository at this point in the history
  • Loading branch information
imouandjolobe-pass committed Nov 26, 2024
1 parent 68648b6 commit a46f29d
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 64 deletions.
1 change: 1 addition & 0 deletions src/shared/offer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type HitOffer = {
prices?: number[]
subcategoryId: SubcategoryIdEnum
thumbUrl?: string
releaseDate?: number | string
}

export interface Offer {
Expand Down
191 changes: 130 additions & 61 deletions src/ui/components/tiles/HorizontalOfferTile.native.test.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import React from 'react'

import { navigate } from '__mocks__/@react-navigation/native'
import { SubcategoriesResponseModelv2 } from 'api/gen'
import { SubcategoriesResponseModelv2, SubcategoryIdEnum } from 'api/gen'
import * as logClickOnProductAPI from 'libs/algolia/analytics/logClickOnOffer'
import { mockedAlgoliaResponse } from 'libs/algolia/fixtures/algoliaFixtures'
import { analytics } from 'libs/analytics'
import { OfferAnalyticsParams } from 'libs/analytics/types'
import { useDistance } from 'libs/location/hooks/useDistance'
import { subcategoriesDataTest } from 'libs/subcategories/fixtures/subcategoriesResponse'
import { Offer } from 'shared/offer/types'
import { mockServer } from 'tests/mswServer'
import { reactQueryProviderHOC } from 'tests/reactQueryProviderHOC'
import { fireEvent, render, screen, waitFor } from 'tests/utils'
import { fireEvent, render, screen } from 'tests/utils'

import { HorizontalOfferTile } from './HorizontalOfferTile'

Expand Down Expand Up @@ -43,20 +44,19 @@ describe('HorizontalOfferTile component', () => {
})

it('should navigate to the offer when pressing an offer', async () => {
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={mockOffer} analyticsParams={mockAnalyticsParams} />
)
)
renderHorizontalOfferTile({
offer: mockOffer,
analyticsParams: mockAnalyticsParams,
})

fireEvent.press(screen.getByRole('link'))

await waitFor(() => {
expect(navigate).toHaveBeenCalledWith('Offer', {
id: offerId,
from: 'searchresults',
searchId: '539b285e',
})
await screen.findByText(mockOffer.offer.name)

expect(navigate).toHaveBeenCalledWith('Offer', {
id: offerId,
from: 'searchresults',
searchId: '539b285e',
})
})

Expand All @@ -68,89 +68,158 @@ describe('HorizontalOfferTile component', () => {
)
fireEvent.press(screen.getByRole('link'))

await waitFor(() => {
expect(analytics.logConsultOffer).toHaveBeenCalledTimes(1)
expect(analytics.logConsultOffer).toHaveBeenCalledWith({
offerId,
from: 'searchresults',
query: '',
index: 0,
searchId: '539b285e',
})
await screen.findByText(mockOffer.offer.name)

expect(analytics.logConsultOffer).toHaveBeenCalledTimes(1)
expect(analytics.logConsultOffer).toHaveBeenCalledWith({
offerId,
from: 'searchresults',
query: '',
index: 0,
searchId: '539b285e',
})
})

it('should notify Algolia when pressing an offer', async () => {
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={mockOffer} analyticsParams={mockAnalyticsParams} />
)
)
renderHorizontalOfferTile({
offer: mockOffer,
analyticsParams: mockAnalyticsParams,
})

const hitComponent = screen.getByRole('link')
fireEvent.press(hitComponent)

await waitFor(() => {
expect(spyLogClickOnOffer).toHaveBeenCalledWith({
objectID: '102280',
position: 0,
})
await screen.findByText(mockOffer.offer.name)

expect(spyLogClickOnOffer).toHaveBeenCalledWith({
objectID: '102280',
position: 0,
})
})

it('should show distance if geolocation enabled', async () => {
mockUseDistance.mockReturnValueOnce('10 km')
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={mockOffer} analyticsParams={mockAnalyticsParams} />
)
)
await waitFor(() => {
expect(screen.getByText('à 10 km')).toBeOnTheScreen()

renderHorizontalOfferTile({
offer: mockOffer,
analyticsParams: mockAnalyticsParams,
})

expect(await screen.findByText('à 10 km')).toBeOnTheScreen()
})

it('should not show distance if user has an unprecise location (type municipality or locality)', async () => {
mockUseDistance.mockReturnValueOnce(null)
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={mockOffer} analyticsParams={mockAnalyticsParams} />
)
)
await waitFor(() => {
expect(screen.getByText('La nuit des temps')).toBeOnTheScreen()
renderHorizontalOfferTile({
offer: mockOffer,
analyticsParams: mockAnalyticsParams,
})

expect(await screen.findByText('La nuit des temps')).toBeOnTheScreen()

expect(screen.queryByText('à 10 km')).not.toBeOnTheScreen()
})

describe('When pressing an offer without object id', () => {
const offer = { ...mockOffer, objectID: '' }

it('should not navigate to the offer', async () => {
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={offer} analyticsParams={mockAnalyticsParams} />
)
)
renderHorizontalOfferTile({
offer,
analyticsParams: mockAnalyticsParams,
})

fireEvent.press(screen.getByRole('link'))

await waitFor(() => {
expect(navigate).not.toHaveBeenCalled()
})
await screen.findByText(mockOffer.offer.name)

expect(navigate).not.toHaveBeenCalled()
})

it('should not log analytics event', async () => {
render(
reactQueryProviderHOC(
<HorizontalOfferTile offer={offer} analyticsParams={mockAnalyticsParams} />
)
)
renderHorizontalOfferTile({
offer,
analyticsParams: mockAnalyticsParams,
})

fireEvent.press(screen.getByRole('link'))

await waitFor(() => {
expect(analytics.logConsultOffer).not.toHaveBeenCalled()
await screen.findByText(mockOffer.offer.name)

expect(analytics.logConsultOffer).not.toHaveBeenCalled()
})
})

describe('When offer is a `SEANCE_CINE`', () => {
const defaultMovieName = 'La petite sirène'
const defaultMovieScreeningOffer = {
...mockOffer.offer,
subcategoryId: SubcategoryIdEnum.SEANCE_CINE,
name: defaultMovieName,
}

it('should format releaseDate when a valid one is given', async () => {
const validReleaseDate = 1722988800 // 7 août 2024

const movieScreeningOfferWithValidReleaseDate = {
...mockOffer,
offer: {
...defaultMovieScreeningOffer,
releaseDate: validReleaseDate,
},
}

renderHorizontalOfferTile({
offer: movieScreeningOfferWithValidReleaseDate,
analyticsParams: mockAnalyticsParams,
})

await screen.findByText(defaultMovieName)

expect(await screen.findByText('Sorti le 7 août 2024')).toBeOnTheScreen()
})

it('should not format releaseDate when an invalid one is given', async () => {
const invalidReleaseDate = '1722988800'
const movieScreeningOfferWithInvalidReleaseDate = {
...mockOffer,
offer: {
...defaultMovieScreeningOffer,
releaseDate: invalidReleaseDate,
},
}

renderHorizontalOfferTile({
offer: movieScreeningOfferWithInvalidReleaseDate,
analyticsParams: mockAnalyticsParams,
})

await screen.findByText(defaultMovieName)

expect(screen.queryByText('Sorti le 7 août 2024')).not.toBeOnTheScreen()
})

it('should format dates when no releaseDate is given', async () => {
const movieScreeningOfferWithoutReleaseDate = {
...mockOffer,
offer: {
...defaultMovieScreeningOffer,
dates: [1732721400], // 27 novembre 2024
},
}

renderHorizontalOfferTile({
offer: movieScreeningOfferWithoutReleaseDate,
analyticsParams: mockAnalyticsParams,
})

await screen.findByText(defaultMovieName)

expect(await screen.findByText('27 novembre 2024')).toBeOnTheScreen()
})
})
})

function renderHorizontalOfferTile(props: { offer: Offer; analyticsParams: OfferAnalyticsParams }) {
return render(reactQueryProviderHOC(<HorizontalOfferTile {...props} />))
}
15 changes: 12 additions & 3 deletions src/ui/components/tiles/HorizontalOfferTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React, { useMemo } from 'react'
import { StyleProp, ViewStyle } from 'react-native'
import styled from 'styled-components/native'

import { SubcategoryIdEnum } from 'api/gen'
import { useLogClickOnOffer } from 'libs/algolia/analytics/logClickOnOffer'
import { triggerConsultOfferLog } from 'libs/analytics/helpers/triggerLogConsultOffer/triggerConsultOfferLog'
import { OfferAnalyticsParams } from 'libs/analytics/types'
import { useDistance } from 'libs/location/hooks/useDistance'
import { formatDates } from 'libs/parsers/formatDates'
import { formatDates, formatReleaseDate } from 'libs/parsers/formatDates'
import { getDisplayPrice } from 'libs/parsers/getDisplayPrice'
import { useSubcategory } from 'libs/subcategories'
import { useSearchGroupLabel } from 'libs/subcategories/useSearchGroupLabel'
Expand Down Expand Up @@ -37,17 +38,25 @@ export const HorizontalOfferTile = ({
...horizontalTileProps
}: Props) => {
const { offer: offerDetails, objectID, _geoloc } = offer
const { subcategoryId, dates, prices, thumbUrl, name } = offerDetails
const { subcategoryId, dates, prices, thumbUrl, name, releaseDate } = offerDetails
const prePopulateOffer = usePrePopulateOffer()
const distanceToOffer = useDistance(_geoloc)
const { categoryId, searchGroupName, nativeCategoryId } = useSubcategory(subcategoryId)
const searchGroupLabel = useSearchGroupLabel(searchGroupName)
const { logClickOnOffer } = useLogClickOnOffer()

const isOfferAMovieScreeningWithAReleaseDate =
subcategoryId === SubcategoryIdEnum.SEANCE_CINE &&
releaseDate &&
typeof releaseDate === 'number' // we do this because for now, some offers' releaseDate attribute have the wrong type

const timestampsInMillis = dates?.map((timestampInSec) => timestampInSec * 1000)
const offerId = Number(objectID)

const formattedDate = formatDates(timestampsInMillis)
const formattedDate = isOfferAMovieScreeningWithAReleaseDate
? formatReleaseDate(releaseDate * 1000)
: formatDates(timestampsInMillis)

const formattedPrice = getDisplayPrice(prices)
const nativeCategoryValue = useNativeCategoryValue({ nativeCategoryId })

Expand Down

0 comments on commit a46f29d

Please sign in to comment.