fix attribution parameters bug and add tests

This commit is contained in:
Francis Cao committed 2026-08-02 13:45:24 -07:00
1 parent d336eba27d
commit 329506cca7
2 files changed
+132 -2

No files matched your search

@@ -0,0 +1,123 @@
import type { ReactNode } from 'react';
import { beforeEach, expect, test, vi } from 'vitest';
import { render, screen } from '@/test/render';
import { AttributionPage } from './AttributionPage';
let mockDateRange = {
startDate: new Date('2026-07-01T00:00:00.000Z'),
endDate: new Date('2026-07-31T23:59:59.999Z'),
};
let mockValues = [{ value: 'Sign up' }, { value: 'Purchase' }];
beforeEach(() => {
mockDateRange = {
startDate: new Date('2026-07-01T00:00:00.000Z'),
endDate: new Date('2026-07-31T23:59:59.999Z'),
};
mockValues = [{ value: 'Sign up' }, { value: 'Purchase' }];
});
vi.mock('@umami/react-zen', () => ({
Column: ({ children }: { children: ReactNode }) => <div>{children}</div>,
Grid: ({ children }: { children: ReactNode }) => <div>{children}</div>,
ListItem: ({ id, children }: { id: string; children: ReactNode }) => (
<option value={id}>{children}</option>
),
Select: ({
label,
value,
defaultValue,
placeholder,
onChange,
children,
}: {
label: string;
value?: string;
defaultValue?: string;
placeholder?: string;
onChange?: (value: string) => void;
children: ReactNode;
}) => (
<label>
{label}
<select
aria-label={label}
value={value ?? defaultValue ?? ''}
onChange={event => onChange?.(event.currentTarget.value)}
>
{placeholder && <option value="">{placeholder}</option>}
{children}
</select>
</label>
),
}));
vi.mock('@/components/common/Empty', () => ({
Empty: () => <div>Empty</div>,
}));
vi.mock('@/components/hooks', () => ({
useDateRange: () => ({
dateRange: mockDateRange,
}),
useMessages: () => ({
t: (value: string) => value,
labels: {
model: 'Model',
type: 'Type',
conversionStep: 'Conversion Step',
firstClick: 'First Click',
lastClick: 'Last Click',
viewedPage: 'Viewed Page',
triggeredEvent: 'Triggered Event',
},
}),
useWebsiteValuesQuery: () => ({
data: mockValues,
isLoading: false,
}),
}));
vi.mock('@/app/(main)/websites/[websiteId]/WebsiteControls', () => ({
WebsiteControls: () => <div>WebsiteControls</div>,
}));
vi.mock('./Attribution', () => ({
Attribution: ({ step, type }: { step: string; type: string }) => (
<div data-testid="attribution-result">{`${type}:${step}`}</div>
),
}));
test('keeps the selected conversion step when the date range refreshes the value list', async () => {
const { rerender, user } = render(<AttributionPage websiteId="website-1" />);
await user.selectOptions(screen.getByLabelText('Conversion Step'), 'Purchase');
expect(screen.getByLabelText('Conversion Step')).toHaveValue('Purchase');
expect(screen.getByTestId('attribution-result')).toHaveTextContent('path:Purchase');
mockDateRange = {
startDate: new Date('2026-08-01T00:00:00.000Z'),
endDate: new Date('2026-08-31T23:59:59.999Z'),
};
mockValues = [{ value: 'Sign up' }];
rerender(<AttributionPage websiteId="website-1" />);
expect(screen.getByLabelText('Conversion Step')).toHaveValue('Purchase');
expect(screen.getByTestId('attribution-result')).toHaveTextContent('path:Purchase');
});
test('clears the selected conversion step when the attribution type changes', async () => {
const { user } = render(<AttributionPage websiteId="website-1" />);
await user.selectOptions(screen.getByLabelText('Conversion Step'), 'Purchase');
expect(screen.getByTestId('attribution-result')).toHaveTextContent('path:Purchase');
await user.selectOptions(screen.getByLabelText('Type'), 'event');
expect(screen.getByLabelText('Conversion Step')).toHaveValue('');
expect(screen.queryByTestId('attribution-result')).not.toBeInTheDocument();
});
@@ -1,6 +1,6 @@
'use client';
import { Column, Grid, ListItem, Select } from '@umami/react-zen';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { Empty } from '@/components/common/Empty';
import { useDateRange, useMessages, useWebsiteValuesQuery } from '@/components/hooks';
@@ -25,6 +25,13 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
endDate,
});
const items = data?.filter(({ value }) => value) || [];
const stepItems = useMemo(() => {
if (!step || items.some(({ value }) => value === step)) {
return items;
}
return [{ value: step }, ...items];
}, [items, step]);
const handleTypeChange = (value: any) => {
setType(value as string);
@@ -71,7 +78,7 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
maxHeight={320}
allowSearch
>
{items.map(({ value }) => (
{stepItems.map(({ value }) => (
<ListItem key={value} id={value}>
{value}
</ListItem>