import { render, screen, userEvent, waitForElementToBeRemoved, } from 'sentry-test/reactTestingLibrary'; import {textWithMarkupMatcher} from 'sentry-test/utils'; import {openModal} from 'sentry/actionCreators/modal'; import GlobalModal from 'sentry/components/globalModal'; import {ServerSideSamplingStore} from 'sentry/stores/serverSideSamplingStore'; import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent'; import {UniformRateModal} from 'sentry/views/settings/project/server-side-sampling/modals/uniformRateModal'; import {SERVER_SIDE_SAMPLING_DOC_LINK} from 'sentry/views/settings/project/server-side-sampling/utils'; import {getMockData, outcomesWithoutClientDiscarded} from '../testUtils'; jest.mock('sentry/utils/analytics/trackAdvancedAnalyticsEvent'); describe('Server-Side Sampling - Uniform Rate Modal', function () { beforeEach(function () { ServerSideSamplingStore.reset(); }); it('render next button', async function () { const {organization, project} = getMockData(); const handleSubmit = jest.fn(); const handleReadDocs = jest.fn(); ServerSideSamplingStore.fetchProjectStats30dSuccess(TestStubs.Outcomes()); ServerSideSamplingStore.fetchProjectStats48hSuccess(TestStubs.Outcomes()); const {container} = render(); openModal(modalProps => ( )); // Header expect( await screen.findByRole('heading', { name: 'Set a global sample rate', }) ).toBeInTheDocument(); expect( screen.getByText( textWithMarkupMatcher( 'Set a server-side sample rate for all transactions using our suggestion as a starting point.' ) ) ).toBeInTheDocument(); // Content expect(screen.getByText('Transactions (Last 30 days)')).toBeInTheDocument(); // Chart expect(screen.getByRole('radio', {name: 'Current'})).toBeChecked(); expect(screen.getByRole('radio', {name: 'Suggested'})).not.toBeChecked(); expect(screen.getByText('100%')).toBeInTheDocument(); // Current client-side sample rate expect(screen.getByText('N/A')).toBeInTheDocument(); // Current server-side sample rate expect(screen.getAllByRole('spinbutton')[0]).toHaveValue(95); // Suggested client-side sample rate expect(screen.queryByTestId('invalid-client-rate')).not.toBeInTheDocument(); // Client input warning is not visible expect(screen.getAllByTestId('more-information')).toHaveLength(2); // Client input help is visible expect(screen.getAllByRole('spinbutton')[1]).toHaveValue(95); // Suggested server-side sample rate expect(screen.queryByLabelText('Reset to suggested values')).not.toBeInTheDocument(); expect(screen.queryByTestId('invalid-server-rate')).not.toBeInTheDocument(); // Server input warning is not visible // Enter invalid client-side sample rate userEvent.clear(screen.getAllByRole('spinbutton')[0]); userEvent.hover(screen.getByTestId('invalid-client-rate')); // Client input warning is visible expect(await screen.findByText('Set a value between 0 and 100')).toBeInTheDocument(); expect(screen.queryByTestId('more-information')).not.toBeInTheDocument(); // Client input help is not visible // Hover over next button userEvent.hover(screen.getByRole('button', {name: 'Next'})); expect(await screen.findByText('Sample rate is not valid')).toBeInTheDocument(); // Enter valid custom client-sample rate userEvent.type(screen.getAllByRole('spinbutton')[0], '20{enter}'); expect(screen.queryByText('Suggested')).not.toBeInTheDocument(); expect(screen.getAllByRole('spinbutton')[0]).toHaveValue(20); // Custom client-side sample rate expect(screen.getByRole('radio', {name: 'New'})).toBeChecked(); expect(screen.getByLabelText('Reset to suggested values')).toBeInTheDocument(); // Enter invalid server-side sample rate userEvent.clear(screen.getAllByRole('spinbutton')[1]); userEvent.hover(screen.getByTestId('invalid-server-rate')); // Server input warning is visible expect(await screen.findByText('Set a value between 0 and 100')).toBeInTheDocument(); // Enter a server-side sample rate higher than the client-side rate userEvent.type(screen.getAllByRole('spinbutton')[1], '30{enter}'); userEvent.hover(screen.getByTestId('invalid-server-rate')); // Server input warning is visible expect( await screen.findByText( 'Server sample rate shall not be higher than client sample rate' ) ).toBeInTheDocument(); // Reset sample rates to suggested values userEvent.click(screen.getByLabelText('Reset to suggested values')); expect(screen.getByText('Suggested')).toBeInTheDocument(); expect(screen.getAllByRole('spinbutton')[0]).toHaveValue(95); // Suggested client-side sample rate expect(screen.getAllByRole('spinbutton')[1]).toHaveValue(95); // Suggested server-side sample rate expect(screen.queryByTestId('invalid-client-rate')).not.toBeInTheDocument(); expect(screen.getAllByTestId('more-information')).toHaveLength(2); // Question marks (help components) are visible expect(screen.queryByTestId('invalid-server-rate')).not.toBeInTheDocument(); // Footer expect(screen.getByRole('button', {name: 'Read Docs'})).toHaveAttribute( 'href', SERVER_SIDE_SAMPLING_DOC_LINK ); expect(screen.getByText('Step 1 of 2')).toBeInTheDocument(); expect(screen.getByRole('button', {name: 'Cancel'})).toBeInTheDocument(); expect(screen.getByRole('button', {name: 'Next'})).toBeInTheDocument(); // Take screenshot (this is good as we can not test the chart) expect(container).toSnapshot(); // Click on docs button userEvent.click(screen.getByRole('button', {name: 'Read Docs'})); expect(handleReadDocs).toHaveBeenCalled(); expect(trackAdvancedAnalyticsEvent).toHaveBeenCalledWith( 'sampling.settings.modal.uniform.rate_read_docs', expect.objectContaining({ organization, project_id: project.id, }) ); // Click on next button userEvent.click(screen.getByRole('button', {name: 'Next'})); expect(handleSubmit).not.toHaveBeenCalled(); expect(trackAdvancedAnalyticsEvent).toHaveBeenCalledWith( 'sampling.settings.modal.uniform.rate_next', expect.objectContaining({ organization, project_id: project.id, }) ); // Click on close button userEvent.click(screen.getByLabelText('Close Modal')); await waitForElementToBeRemoved(() => screen.queryByLabelText('Close Modal')); }); it('render done button', async function () { ServerSideSamplingStore.fetchProjectStats30dSuccess(TestStubs.Outcomes()); ServerSideSamplingStore.fetchProjectStats48hSuccess({ ...TestStubs.Outcomes(), groups: [], }); const {organization, project} = getMockData(); const handleSubmit = jest.fn(); const {container} = render(); openModal(modalProps => ( )); // Content const suggestedSampleRates = await screen.findAllByRole('spinbutton'); expect(suggestedSampleRates[0]).toHaveValue(100); // Suggested client-side sample rate expect(suggestedSampleRates[1]).toHaveValue(100); // Suggested server-side sample rate expect(trackAdvancedAnalyticsEvent).toHaveBeenCalledWith( 'sampling.settings.modal.uniform.rate_switch_current', expect.objectContaining({ organization, project_id: project.id, }) ); // Footer expect(screen.getByRole('button', {name: 'Done'})).toBeDisabled(); expect(screen.queryByText('Step 1 of 2')).not.toBeInTheDocument(); // Hover over done button userEvent.hover(screen.getByRole('button', {name: 'Done'})); expect( await screen.findByText('Current sampling values selected') ).toBeInTheDocument(); // Switch to suggested sample rates userEvent.click(screen.getByText('Suggested')); expect(screen.getByRole('button', {name: 'Done'})).toBeEnabled(); expect(trackAdvancedAnalyticsEvent).toHaveBeenCalledWith( 'sampling.settings.modal.uniform.rate_switch_recommended', expect.objectContaining({ organization, project_id: project.id, }) ); // Take screenshot (this is good as we can not test the chart) expect(container).toSnapshot(); // Submit userEvent.click(screen.getByRole('button', {name: 'Done'})); expect(handleSubmit).toHaveBeenCalledWith( expect.objectContaining({ sampleRate: 1, recommendedSampleRate: true, uniformRateModalOrigin: true, rule: undefined, }) ); // Click on close button userEvent.click(screen.getByLabelText('Close Modal')); await waitForElementToBeRemoved(() => screen.queryByLabelText('Done')); }); it('cancel flow', async function () { ServerSideSamplingStore.fetchProjectStats30dSuccess(TestStubs.Outcomes()); ServerSideSamplingStore.fetchProjectStats48hSuccess({ ...TestStubs.Outcomes(), groups: [], }); const {organization, project} = getMockData(); render(); openModal(modalProps => ( )); await screen.findByRole('heading', {name: 'Set a global sample rate'}); // Cancel userEvent.click(screen.getByRole('button', {name: 'Cancel'})); await waitForElementToBeRemoved(() => screen.queryByLabelText('Cancel')); expect(trackAdvancedAnalyticsEvent).toHaveBeenCalledWith( 'sampling.settings.modal.uniform.rate_cancel', expect.objectContaining({ organization, project_id: project.id, }) ); }); it('display "Specify client rate modal" content as a first step', async function () { ServerSideSamplingStore.fetchProjectStats30dSuccess(outcomesWithoutClientDiscarded); ServerSideSamplingStore.fetchProjectStats48hSuccess(outcomesWithoutClientDiscarded); const {organization, project} = getMockData(); render(); openModal(modalProps => ( )); expect( await screen.findByRole('heading', { name: 'Specify current client(SDK) sample rate', }) ).toBeInTheDocument(); expect(screen.getByRole('button', {name: 'Next'})).toBeDisabled(); // Enter valid specified client-sample rate userEvent.type(screen.getByRole('spinbutton'), '0.2{enter}'); userEvent.click(screen.getByRole('button', {name: 'Next'})); expect( await screen.findByRole('heading', {name: 'Set a global sample rate'}) ).toBeInTheDocument(); // Content expect(screen.getByText('20%')).toBeInTheDocument(); // Current client-side sample rate expect(screen.getByText('N/A')).toBeInTheDocument(); // Current server-side sample rate expect(screen.getAllByRole('spinbutton')[0]).toHaveValue(100); // Suggested client-side sample rate expect(screen.getAllByRole('spinbutton')[1]).toHaveValue(20); // Suggested server-side sample rate // Footer expect(screen.getByText('Step 2 of 3')).toBeInTheDocument(); // Go Back userEvent.click(screen.getByRole('button', {name: 'Back'})); // Specified sample rate has to still be there expect(screen.getByRole('spinbutton')).toHaveValue(0.2); // Close Modal userEvent.click(screen.getByRole('button', {name: 'Cancel'})); await waitForElementToBeRemoved(() => screen.queryByLabelText('Cancel')); }); it('does not display "Specify client rate modal" if no groups', async function () { ServerSideSamplingStore.fetchProjectStats30dSuccess(TestStubs.Outcomes()); ServerSideSamplingStore.fetchProjectStats48hSuccess({ ...outcomesWithoutClientDiscarded, groups: [], }); const {organization, project} = getMockData(); render(); openModal(modalProps => ( )); expect( await screen.findByRole('heading', {name: 'Set a global sample rate'}) ).toBeInTheDocument(); // Close Modal userEvent.click(screen.getByRole('button', {name: 'Cancel'})); await waitForElementToBeRemoved(() => screen.queryByLabelText('Cancel')); }); it('display request error message', async function () { ServerSideSamplingStore.fetchProjectStats30dError('some error'); ServerSideSamplingStore.fetchProjectStats48hError('some error'); const {organization, project} = getMockData(); render(); openModal(modalProps => ( )); expect( await screen.findByText(/There was an error loading data/) ).toBeInTheDocument(); }); });