Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { MockedProvider } from '@apollo/client/testing'
import { render, screen, waitFor } from '@testing-library/react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { formatISO } from 'date-fns'

import { EditorProvider } from '@core/journeys/ui/EditorProvider'
import { JourneyProvider } from '@core/journeys/ui/JourneyProvider'
Expand All @@ -8,12 +10,22 @@ import { getJourneyAnalytics } from '@core/journeys/ui/useJourneyAnalyticsQuery/
import { GetJourney_journey } from '../../../../../../__generated__/GetJourney'

import { earliestStatsCollected } from './AnalyticsOverlaySwitch'
import { buildPlausibleDateRange } from './buildPlausibleDateRange'

import { AnalyticsOverlaySwitch } from '.'

jest.mock('./buildPlausibleDateRange')

const mockBuildPlausibleDateRange =
buildPlausibleDateRange as jest.MockedFunction<typeof buildPlausibleDateRange>

const mockCurrentDate = '2024-06-02'

describe('AnalyticsOverlaySwitch', () => {
beforeEach(() => {
mockBuildPlausibleDateRange.mockClear()
})

beforeAll(() => {
jest.useFakeTimers()
jest.setSystemTime(new Date('2024-06-02'))
Expand All @@ -24,6 +36,9 @@ describe('AnalyticsOverlaySwitch', () => {
})

it('toggles showAnalytics', async () => {
mockBuildPlausibleDateRange.mockReturnValue(
`${earliestStatsCollected},${mockCurrentDate}`
)
const result = jest.fn().mockReturnValue(getJourneyAnalytics.result)
const journey = { id: 'journeyId' } as unknown as GetJourney_journey
const request = {
Expand Down Expand Up @@ -78,4 +93,55 @@ describe('AnalyticsOverlaySwitch', () => {
screen.getByRole('checkbox').click()
expect(showAnalytics).toHaveTextContent('false')
})

it('gets analytics for selected date range', async () => {
const selectedStartDate = new Date('2024-06-05')
const selectedEndDate = new Date('2024-06-10')
const formattedDateRange = `${formatISO(selectedStartDate, {
representation: 'date'
})},${formatISO(selectedEndDate, { representation: 'date' })}`

// Pretend the date picker has already produced our custom range
mockBuildPlausibleDateRange.mockReturnValue(formattedDateRange)

const result = jest.fn().mockReturnValue(getJourneyAnalytics.result)
const journey = { id: 'journeyId' } as unknown as GetJourney_journey

render(
<MockedProvider
mocks={[
{
request: {
...getJourneyAnalytics.request,
variables: {
...getJourneyAnalytics.request.variables,
period: 'custom',
date: formattedDateRange
}
},
result
}
]}
>
<JourneyProvider value={{ journey }}>
<EditorProvider>
{({ state: { analytics } }) => (
<>
<div data-testid="analytics">{JSON.stringify(analytics)}</div>
<AnalyticsOverlaySwitch />
</>
)}
</EditorProvider>
</JourneyProvider>
</MockedProvider>
)

const analyticsCheckbox = screen.getByRole('checkbox')
fireEvent.click(analyticsCheckbox)

// Verify the network call was made with the mocked date range
await waitFor(() => {
expect(result).toHaveBeenCalled()
})
})
})
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import FormControlLabel from '@mui/material/FormControlLabel'
import Stack from '@mui/material/Stack'
import Switch from '@mui/material/Switch'
import Typography from '@mui/material/Typography'
import { formatISO } from 'date-fns'
import { useTranslation } from 'next-i18next'
import { enqueueSnackbar } from 'notistack'
import { ReactElement } from 'react'
import { ReactElement, useState } from 'react'

import { useEditor } from '@core/journeys/ui/EditorProvider'
import { useJourney } from '@core/journeys/ui/JourneyProvider'
import { useJourneyAnalyticsQuery } from '@core/journeys/ui/useJourneyAnalyticsQuery'

import { DateRangePicker } from '../../../../JourneyVisitorsList/FilterDrawer/ExportDialog/DateRangePicker'

import { buildPlausibleDateRange } from './buildPlausibleDateRange'

// Used to for filter all time stats
export const earliestStatsCollected = '2024-06-01'

Expand All @@ -20,22 +24,46 @@ export function AnalyticsOverlaySwitch(): ReactElement {
state: { showAnalytics },
dispatch
} = useEditor()
const currentDate = formatISO(new Date(), { representation: 'date' })

const [startDate, setStartDate] = useState<Date | null>(
new Date(earliestStatsCollected)
)
const [endDate, setEndDate] = useState<Date | null>(new Date())

const isDateRangeValid =
startDate != null && endDate != null && startDate <= endDate

const formattedDateRange = buildPlausibleDateRange(
startDate,
endDate,
earliestStatsCollected,
new Date()
)

useJourneyAnalyticsQuery({
variables: {
id: journey?.id ?? '',
period: 'custom',
date: `${earliestStatsCollected},${currentDate}`
date: formattedDateRange
},
skip: journey?.id == null || showAnalytics !== true,
skip:
journey?.id == null ||
showAnalytics !== true ||
isDateRangeValid !== true,
onCompleted: (analytics) => {
dispatch({
type: 'SetAnalyticsAction',
analytics
})
},
onError: (_) => {
if (isDateRangeValid) {
enqueueSnackbar(t('Invalid date range'), {
variant: 'error',
preventDuplicate: true
})
return
}
enqueueSnackbar(t('Error fetching analytics'), {
variant: 'error',
preventDuplicate: true
Expand All @@ -51,17 +79,27 @@ export function AnalyticsOverlaySwitch(): ReactElement {
}

return (
<FormControlLabel
control={
<Switch
checked={showAnalytics === true}
onChange={handleSwitchAnalytics}
<Stack direction="column" spacing={2} alignItems="flex-start">
<FormControlLabel
control={
<Switch
checked={showAnalytics === true}
onChange={handleSwitchAnalytics}
/>
}
label={
<Typography variant="subtitle2">{t('Analytics Overlay')}</Typography>
}
labelPlacement="start"
/>
{showAnalytics && (
<DateRangePicker
startDate={startDate}
endDate={endDate}
onStartDateChange={setStartDate}
onEndDateChange={setEndDate}
/>
}
label={
<Typography variant="subtitle2">{t('Analytics Overlay')}</Typography>
}
labelPlacement="start"
/>
)}
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Ensure we use the real formatISO implementation, not a mock from other tests
import { formatISO } from 'date-fns'

import { buildPlausibleDateRange } from './buildPlausibleDateRange'

describe('buildPlausibleDateRange', () => {
const fallbackStart = '2024-06-01'
const fallbackEnd = new Date('2024-06-10')

it('returns formatted range when start and end dates are valid', () => {
const startDate = new Date('2024-06-02')
const endDate = new Date('2024-06-05')

const result = buildPlausibleDateRange(
startDate,
endDate,
fallbackStart,
fallbackEnd
)

expect(result).toBe(
`${formatISO(startDate, { representation: 'date' })},${formatISO(endDate, { representation: 'date' })}`
)
})

it('falls back when dates are null', () => {
const result = buildPlausibleDateRange(
null,
null,
fallbackStart,
fallbackEnd
)

expect(result).toBe(
`${fallbackStart},${formatISO(fallbackEnd, { representation: 'date' })}`
)
})

it('falls back when start date is after end date', () => {
const startDate = new Date('2024-06-06')
const endDate = new Date('2024-06-05')

const result = buildPlausibleDateRange(
startDate,
endDate,
fallbackStart,
fallbackEnd
)

expect(result).toBe(
`${formatISO(fallbackStart, { representation: 'date' })},${formatISO(fallbackEnd, { representation: 'date' })}`
)
Comment on lines +50 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test expectation is incorrect — formatISO is called on a string.

fallbackStart is a string ('2024-06-01'), but formatISO expects a Date object. The implementation in buildPlausibleDateRange.ts uses fallbackStartDate directly as a string without formatting.

This test will fail because formatISO(fallbackStart, ...) will throw or produce unexpected output.

🐛 Proposed fix
     expect(result).toBe(
-      `${formatISO(fallbackStart, { representation: 'date' })},${formatISO(fallbackEnd, { representation: 'date' })}`
+      `${fallbackStart},${formatISO(fallbackEnd, { representation: 'date' })}`
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(result).toBe(
`${formatISO(fallbackStart, { representation: 'date' })},${formatISO(fallbackEnd, { representation: 'date' })}`
)
expect(result).toBe(
`${fallbackStart},${formatISO(fallbackEnd, { representation: 'date' })}`
)
🤖 Prompt for AI Agents
In
`@apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.spec.ts`
around lines 50 - 52, The test in buildPlausibleDateRange.spec.ts calls
formatISO on fallbackStart/fallbackEnd which are strings; update the expectation
to match the implementation by either converting those string fixtures to Date
objects before calling formatISO or (simpler) assert the raw string returned by
buildPlausibleDateRange: replace formatISO(fallbackStart/End, ...) with the
plain fallbackStart and fallbackEnd values so the expectation matches the
buildPlausibleDateRange behavior (refer to fallbackStart, fallbackEnd and the
buildPlausibleDateRange test case).

})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { formatISO } from 'date-fns'

export function buildPlausibleDateRange(
startDate: Date | null,
endDate: Date | null,
fallbackStartDate: string,
fallbackEndDate: Date
): string {
if (startDate != null && endDate != null && startDate <= endDate) {
return `${formatISO(startDate, {
representation: 'date'
})},${formatISO(endDate, { representation: 'date' })}`
}

return `${fallbackStartDate},${formatISO(fallbackEndDate, {
representation: 'date'
})}`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { buildPlausibleDateRange } from './buildPlausibleDateRange'
1 change: 1 addition & 0 deletions libs/locales/en/apps-journeys-admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
"Headline": "Headline",
"Secondary Text": "Secondary Text",
"Social Post View": "Social Post View",
"Invalid date range": "Invalid date range",
"Error fetching analytics": "Error fetching analytics",
"Analytics Overlay": "Analytics Overlay",
"Zoom in": "Zoom in",
Expand Down