Skip to content
Merged
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
Expand Up @@ -26,13 +26,13 @@ import {useChartInterval} from 'sentry/utils/useChartInterval';
interface DroppedDataDrawerProps {
dataset: DiscoverDatasets;
interval?: string;
onInvestigate?: (row: ReasonRow) => void;
onTroubleshoot?: (row: ReasonRow) => void;
}

export function DroppedDataDrawer({
dataset,
interval,
onInvestigate,
onTroubleshoot,
}: DroppedDataDrawerProps) {
const theme = useTheme();
const [chartInterval] = useChartInterval();
Expand Down Expand Up @@ -87,7 +87,7 @@ export function DroppedDataDrawer({
<DroppedDataOutcomeList
sections={selectedSections}
colors={outcomeColors}
onInvestigate={onInvestigate}
onTroubleshoot={onTroubleshoot}
/>
</Stack>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import type {DroppedEventsBucket} from 'sentry/components/droppedData/types';
function OutcomeList({
droppedEvents,
acceptedEvents,
onInvestigate,
onTroubleshoot,
}: {
acceptedEvents: DroppedEventsBucket[];
droppedEvents: DroppedEventsBucket[];
onInvestigate?: (row: ReasonRow) => void;
onTroubleshoot?: (row: ReasonRow) => void;
}) {
return (
<DroppedDataOutcomeList
sections={droppedEventsToOutcomeSections(droppedEvents, acceptedEvents)}
colors={{}}
onInvestigate={onInvestigate}
onTroubleshoot={onTroubleshoot}
/>
);
}
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('DroppedDataOutcomeList', () => {
expect(screen.queryByText(/ of /)).not.toBeInTheDocument();
});

it('renders the short description under the reason title', () => {
it('shows the short description when hovering the reason title', async () => {
render(
<OutcomeList
droppedEvents={[
Expand All @@ -110,12 +110,15 @@ describe('DroppedDataOutcomeList', () => {
/>
);

expect(
screen.getByText("User agent matched Sentry's known crawler list.")
).toBeInTheDocument();
const description = "User agent matched Sentry's known crawler list.";
expect(screen.queryByText(description)).not.toBeInTheDocument();

await userEvent.hover(screen.getByText('Web crawler filter'));

expect(await screen.findByText(description)).toBeInTheDocument();
});

it('fills the data type into the description from the event category', () => {
it('fills the data type into the description from the event category', async () => {
render(
<OutcomeList
droppedEvents={[
Expand All @@ -132,8 +135,10 @@ describe('DroppedDataOutcomeList', () => {
/>
);

await userEvent.hover(screen.getByText('Project abuse limit'));

expect(
screen.getByText('Your log events exceeded the project abuse limit.')
await screen.findByText('Your log events exceeded the project abuse limit.')
).toBeInTheDocument();
});

Expand Down Expand Up @@ -192,7 +197,8 @@ describe('DroppedDataOutcomeList', () => {
expect(screen.queryByRole('table')).not.toBeInTheDocument();
});

it('reveals the fix options under a reason row via its chevron', async () => {
it('opens the reason actions menu with docs and troubleshoot entries', async () => {
const onTroubleshoot = jest.fn();
render(
<OutcomeList
droppedEvents={[
Expand All @@ -206,92 +212,28 @@ describe('DroppedDataOutcomeList', () => {
}),
]}
acceptedEvents={[]}
onTroubleshoot={onTroubleshoot}
/>
);

// The fix-this affordance is hidden until the reason chevron is clicked.
expect(screen.queryByRole('button', {name: 'Learn more'})).not.toBeInTheDocument();

await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'}));

expect(screen.getByRole('button', {name: 'Learn more'})).toBeInTheDocument();

// Collapsing hides it again.
await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'}));
expect(screen.queryByRole('button', {name: 'Learn more'})).not.toBeInTheDocument();

// The toggle is reachable from the keyboard.
act(() => screen.getByRole('button', {name: 'Toggle fix options'}).focus());
await userEvent.keyboard('{Enter}');
expect(screen.getByRole('button', {name: 'Learn more'})).toBeInTheDocument();
});

it('shows the long description when a reason row is expanded', async () => {
render(
<OutcomeList
droppedEvents={[
DroppedEventFixture({
outcome: 'client_discard',
reason: 'send_error',
start: 0,
end: 60_000,
count: 10,
}),
]}
acceptedEvents={[]}
/>
);

const longDescription =
'The event reached Sentry but was rejected with an error code.';
expect(screen.queryByText(longDescription)).not.toBeInTheDocument();

await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'}));

expect(screen.getByText(longDescription)).toBeInTheDocument();
});

it('opens the fix-this menu with investigate, settings, and docs entries', async () => {
const onInvestigate = jest.fn();
render(
<OutcomeList
droppedEvents={[
DroppedEventFixture({
category: 'span',
outcome: 'rate_limited',
reason: 'smart_rate_limit',
start: 0,
end: 60_000,
count: 10,
}),
]}
acceptedEvents={[]}
onInvestigate={onInvestigate}
/>
);

await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'}));
await userEvent.click(screen.getByRole('button', {name: 'Learn more'}));

// Project settings deep-links to the usage stats page scoped to the
// reason's data category.
expect(screen.getByRole('menuitemradio', {name: 'Project Settings'})).toHaveAttribute(
'href',
expect.stringContaining('/stats/?dataCategory=spans')
);
await userEvent.click(screen.getByRole('button', {name: 'Reason actions'}));

expect(screen.getAllByRole('menuitemradio').map(item => item.textContent)).toEqual([
'Troubleshoot',
'Go to Docs',
]);
expect(screen.getByRole('menuitemradio', {name: 'Go to Docs'})).toHaveAttribute(
'href',
expect.stringContaining('docs.sentry.io')
);

await userEvent.click(screen.getByRole('menuitemradio', {name: 'Investigate'}));
expect(onInvestigate).toHaveBeenCalledWith(
await userEvent.click(screen.getByRole('menuitemradio', {name: 'Troubleshoot'}));
expect(onTroubleshoot).toHaveBeenCalledWith(
expect.objectContaining({outcome: 'rate_limited', reason: 'smart_rate_limit'})
);
});

it('hides investigate when there is no Seer hand-off', async () => {
it('hides troubleshoot when there is no Seer hand-off', async () => {
render(
<OutcomeList
droppedEvents={[
Expand All @@ -308,14 +250,11 @@ describe('DroppedDataOutcomeList', () => {
/>
);

await userEvent.click(screen.getByRole('button', {name: 'Toggle fix options'}));
await userEvent.click(screen.getByRole('button', {name: 'Learn more'}));
await userEvent.click(screen.getByRole('button', {name: 'Reason actions'}));

expect(screen.getByRole('menuitemradio', {name: 'Go to Docs'})).toBeInTheDocument();
expect(
screen.getByRole('menuitemradio', {name: 'Project Settings'})
).toBeInTheDocument();
expect(
screen.queryByRole('menuitemradio', {name: 'Investigate'})
screen.queryByRole('menuitemradio', {name: 'Troubleshoot'})
).not.toBeInTheDocument();
});
});
Loading
Loading