123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- import {
- screen,
- userEvent,
- waitForElementToBeRemoved,
- within,
- } from 'sentry-test/reactTestingLibrary';
- import {textWithMarkupMatcher} from 'sentry-test/utils';
- import {SamplingInnerName, SamplingRuleType} from 'sentry/types/sampling';
- import {
- distributedTracesConditions,
- individualTransactionsConditions,
- } from 'sentry/views/settings/project/sampling/modal/utils';
- import {
- getInnerNameLabel,
- LEGACY_BROWSER_LIST,
- } from 'sentry/views/settings/project/sampling/utils';
- import {openSamplingRuleModal, renderComponent} from './utils';
- describe('Sampling - Modal', function () {
- beforeEach(function () {
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'GET',
- body: TestStubs.Project(),
- });
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/tags/',
- body: TestStubs.Tags,
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/tags/release/values/',
- method: 'GET',
- body: [{value: '1.2.3'}],
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- it('saves distributed traces rule', async function () {
- const rule = {
- condition: {
- inner: [{name: 'trace.release', op: 'glob', value: ['1.2.3']}],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.2,
- type: 'trace',
- };
- const saveMock = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'PUT',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [rule],
- next_id: 41,
- },
- }),
- });
- renderComponent({ruleType: SamplingRuleType.TRACE});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- const dialog = screen.getByRole('dialog');
- // Modal description
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- 'Using a Trace ID, select all Transactions distributed across multiple projects/services which match your conditions. However, if you only want to select Transactions from within this project, we recommend you add a Individual Transaction rule instead.'
- )
- )
- ).toBeInTheDocument();
- expect(screen.getByRole('link', {name: 'Individual Transaction'})).toHaveAttribute(
- 'href',
- `${SamplingRuleType.TRANSACTION}/`
- );
- // Empty conditions message
- expect(screen.getByText('No conditions added')).toBeInTheDocument();
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- "if you don't want to add (+) a condition, simply, add a sample rate below"
- )
- )
- ).toBeInTheDocument();
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Autocomplete
- expect(screen.getByText(/filter conditions/i)).toBeInTheDocument();
- // Distributed Traces Options
- distributedTracesConditions.forEach(condition => {
- expect(within(dialog).getByText(getInnerNameLabel(condition))).toBeInTheDocument();
- });
- expect(
- within(dialog).queryByText(
- getInnerNameLabel(SamplingInnerName.EVENT_LEGACY_BROWSER)
- )
- ).not.toBeInTheDocument();
- // Click on the condition option
- userEvent.click(screen.getAllByText('Release')[0]);
- // Release field is empty
- expect(screen.queryByTestId('multivalue')).not.toBeInTheDocument();
- // Type into release field
- userEvent.paste(screen.getByLabelText('Search or add a release'), '1.2.3');
- // Autocomplete suggests options
- expect(screen.getByTestId('1.2.3')).toHaveTextContent('1.2.3');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('1.2.3'));
- // Button is still disabled
- expect(screen.getByLabelText('Save Rule')).toBeDisabled();
- // Fill sample rate field
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '20');
- // Save button is now enabled
- expect(screen.getByLabelText('Save Rule')).toBeEnabled();
- // Click on save button
- userEvent.click(screen.getByLabelText('Save Rule'));
- // Close Modal
- userEvent.click(screen.getByLabelText('Close Modal'));
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Add Distributed Trace Rule')
- );
- expect(saveMock).toHaveBeenLastCalledWith(
- '/projects/org-slug/project-slug/',
- expect.objectContaining({
- data: {
- dynamicSampling: {
- rules: [rule],
- },
- },
- })
- );
- expect(screen.getByText('Release')).toBeInTheDocument();
- expect(screen.getByText('1.2.3')).toBeInTheDocument();
- expect(screen.getByText('20%')).toBeInTheDocument();
- });
- it('saves individual transactions rule', async function () {
- const rule = {
- condition: {
- inner: [{name: 'event.release', op: 'glob', value: ['1.2.3']}],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.3,
- type: 'transaction',
- };
- const saveMock = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'PUT',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [rule],
- next_id: 41,
- },
- }),
- });
- renderComponent({ruleType: SamplingRuleType.TRANSACTION});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- const dialog = screen.getByRole('dialog');
- // Modal description
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- 'Select Transactions only within this project which match your conditions. However, If you want to select all Transactions distributed across multiple projects/services, we recommend you add a Distributed Trace rule instead.'
- )
- )
- ).toBeInTheDocument();
- expect(screen.getByRole('link', {name: 'Distributed Trace'})).toHaveAttribute(
- 'href',
- `${SamplingRuleType.TRACE}/`
- );
- // Empty conditions message
- expect(screen.getByText('No conditions added')).toBeInTheDocument();
- expect(
- screen.getByText(
- textWithMarkupMatcher(
- "if you don't want to add (+) a condition, simply, add a sample rate below"
- )
- )
- ).toBeInTheDocument();
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Individual Transactions Options
- individualTransactionsConditions.forEach(condition => {
- expect(within(dialog).getByText(getInnerNameLabel(condition))).toBeInTheDocument();
- });
- // Click on the first condition option
- userEvent.click(screen.getAllByText('Release')[0]);
- // Type into release field
- userEvent.paste(screen.getByLabelText('Search or add a release'), '1.2.3');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('1.2.3'));
- // Fill sample rate field
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '30');
- // Click on save button
- userEvent.click(screen.getByLabelText('Save Rule'));
- // Close Modal
- userEvent.click(screen.getByLabelText('Close Modal'));
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Add Individual Transaction Rule')
- );
- expect(saveMock).toHaveBeenLastCalledWith(
- '/projects/org-slug/project-slug/',
- expect.objectContaining({
- data: {
- dynamicSampling: {
- rules: [rule],
- },
- },
- })
- );
- });
- it('edits the rule', async function () {
- const newRule = {
- condition: {
- inner: [{name: 'trace.release', op: 'glob', value: ['1.2.3']}],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.6,
- type: 'trace',
- };
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'GET',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [
- {
- sampleRate: 0.2,
- type: 'trace',
- condition: {
- op: 'and',
- inner: [
- {
- op: 'glob',
- name: 'trace.release',
- value: ['1.2.2'],
- },
- ],
- },
- id: 40,
- },
- ],
- next_id: 41,
- },
- }),
- });
- const saveMock = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'PUT',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [newRule],
- next_id: 42,
- },
- }),
- });
- renderComponent();
- expect(screen.getByText('1.2.2')).toBeInTheDocument();
- expect(screen.getByText('20%')).toBeInTheDocument();
- await openSamplingRuleModal(screen.getByLabelText('Edit Rule'));
- // Empty conditions message is not displayed
- expect(screen.queryByText('No conditions added')).not.toBeInTheDocument();
- // Type into realease field
- userEvent.clear(screen.getByLabelText('Search or add a release'));
- userEvent.paste(screen.getByLabelText('Search or add a release'), '1.2.3');
- // Click on the suggested option
- userEvent.click(await screen.findByText(textWithMarkupMatcher('Add "1.2.3"')));
- // Update sample rate field
- userEvent.clear(screen.getByPlaceholderText('\u0025'));
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '60');
- // Click on save button
- userEvent.click(screen.getByLabelText('Save Rule'));
- // Modal will close
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Edit Distributed Trace Rule')
- );
- expect(saveMock).toHaveBeenLastCalledWith(
- '/projects/org-slug/project-slug/',
- expect.objectContaining({
- data: {
- dynamicSampling: {
- rules: [newRule],
- },
- },
- })
- );
- // Old values
- expect(screen.queryByText('1.2.2')).not.toBeInTheDocument();
- expect(screen.queryByText('20%')).not.toBeInTheDocument();
- // New values
- expect(screen.getByText('1.2.3')).toBeInTheDocument();
- expect(screen.getByText('60%')).toBeInTheDocument();
- });
- it('legacy browsers condition', async function () {
- const rule = {
- condition: {
- inner: [
- {
- name: 'event.legacy_browser',
- op: 'custom',
- value: [
- 'ie_pre_9',
- 'ie9',
- 'ie10',
- 'ie11',
- 'safari_pre_6',
- 'opera_pre_15',
- 'opera_mini_pre_8',
- 'android_pre_4',
- ],
- },
- ],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.2,
- type: 'transaction',
- };
- const saveMock = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'PUT',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [rule],
- next_id: 43,
- },
- }),
- });
- renderComponent({ruleType: SamplingRuleType.TRANSACTION});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Select Legacy Browser
- userEvent.click(screen.getByText('Legacy Browser'));
- // Legacy Browsers
- expect(screen.getByText('All browsers')).toBeInTheDocument();
- const legacyBrowsers = Object.keys(LEGACY_BROWSER_LIST);
- for (const legacyBrowser of legacyBrowsers) {
- const {icon, title} = LEGACY_BROWSER_LIST[legacyBrowser];
- expect(screen.getByText(title)).toBeInTheDocument();
- expect(screen.getAllByTestId(`icon-${icon}`)[0]).toBeInTheDocument();
- }
- expect(screen.getAllByTestId('icon-internet-explorer')).toHaveLength(4);
- expect(screen.getAllByTestId('icon-opera')).toHaveLength(2);
- expect(screen.getByTestId('icon-safari')).toBeInTheDocument();
- expect(screen.getByTestId('icon-android')).toBeInTheDocument();
- const switchButtons = screen.getAllByTestId('switch');
- expect(switchButtons).toHaveLength(legacyBrowsers.length + 1);
- // All browsers are unchecked
- for (const switchButton of switchButtons) {
- expect(switchButton).not.toBeChecked();
- }
- // Click on the switch of 'All browsers' option
- userEvent.click(switchButtons[0]);
- // All browsers are now checked
- for (const switchButton of switchButtons) {
- expect(switchButton).toBeChecked();
- }
- // Fill sample rate field
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '20');
- // Click on save button
- userEvent.click(screen.getByLabelText('Save Rule'));
- // Close Modal
- userEvent.click(screen.getByLabelText('Close Modal'));
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Add Individual Transaction Rule')
- );
- expect(saveMock).toHaveBeenLastCalledWith(
- '/projects/org-slug/project-slug/',
- expect.objectContaining({
- data: {
- dynamicSampling: {
- rules: [rule],
- },
- },
- })
- );
- // Transaction rules panel is updated
- expect(screen.getByText('Legacy Browser')).toBeInTheDocument();
- for (const legacyBrowser of legacyBrowsers) {
- const {title} = LEGACY_BROWSER_LIST[legacyBrowser];
- expect(screen.getByText(title)).toBeInTheDocument();
- }
- });
- it('custom tag condition', async function () {
- const rule = {
- condition: {
- inner: [{name: 'event.tags.user', op: 'glob', value: ['david']}],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.15,
- type: 'transaction',
- };
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/tags/user/values/',
- method: 'GET',
- body: [{value: 'david'}],
- });
- const saveMock = MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'PUT',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [rule],
- next_id: 43,
- },
- }),
- });
- renderComponent({ruleType: SamplingRuleType.TRANSACTION});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Select Custom Tag
- userEvent.click(screen.getByText('Add Custom Tag'));
- // Type into tag field
- userEvent.paste(screen.getByLabelText('Search or add a tag'), 'user');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('user'));
- // Type into tag value field
- userEvent.paste(screen.getByLabelText('Search or add tag values'), 'david');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('david'));
- // Fill sample rate field
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '15');
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Custom tag condition is added to the conditions dropdown
- expect(screen.getByText('user - Custom')).toBeInTheDocument();
- // Click on save button
- userEvent.click(screen.getByLabelText('Save Rule'));
- // Close Modal
- userEvent.click(screen.getByLabelText('Close Modal'));
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Add Individual Transaction Rule')
- );
- expect(saveMock).toHaveBeenLastCalledWith(
- '/projects/org-slug/project-slug/',
- expect.objectContaining({
- data: {
- dynamicSampling: {
- rules: [rule],
- },
- },
- })
- );
- });
- it('invalid custom tag condition', async function () {
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/tags/sentry.key/values/',
- method: 'GET',
- body: [],
- });
- renderComponent({ruleType: SamplingRuleType.TRANSACTION});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Select Custom Tag
- userEvent.click(screen.getByText('Add Custom Tag'));
- // Type invalid value into tag field
- userEvent.paste(screen.getByLabelText('Search or add a tag'), 'sentry.*');
- // Dropdown display 'no options' because the tag is invalid
- expect(await screen.findByText('No options')).toBeInTheDocument();
- // Type valid value into tag field
- userEvent.type(screen.getByLabelText('Search or add a tag'), '{backspace}key');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('sentry.key'));
- // Type invalid value into tag value field
- userEvent.paste(screen.getByLabelText('Search or add tag values'), 'invalid\\nvalue');
- // Dropdown display 'no options' because the tag value is invalid
- expect(await screen.findByText('No options')).toBeInTheDocument();
- // Clears tag value field
- userEvent.clear(screen.getByLabelText('Search or add tag values'));
- // Type valid value into tag value field
- userEvent.paste(screen.getByLabelText('Search or add tag values'), 'valid');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('valid'));
- });
- it('does not let you save without permissions', async function () {
- renderComponent({
- orgOptions: {
- features: ['filters-and-sampling'],
- access: [],
- },
- });
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Click on 'Add condition'
- userEvent.click(screen.getByText('Add Condition'));
- // Click on the condition option
- userEvent.click(screen.getAllByText('Release')[0]);
- // Type into release field
- userEvent.paste(screen.getByLabelText('Search or add a release'), '1.2.3');
- // Click on the suggested option
- userEvent.click(screen.getByTestId('1.2.3'));
- // Fill sample rate field
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '20');
- // Button is still disabled
- expect(screen.getByLabelText('Save Rule')).toBeDisabled();
- // Close Modal
- userEvent.click(screen.getByLabelText('Close Modal'));
- await waitForElementToBeRemoved(() =>
- screen.queryByText('Add Distributed Trace Rule')
- );
- });
- it('does not let you save a distributed trace rule without a condition, if a trace rule without a condition already exists', async function () {
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'GET',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [
- {
- sampleRate: 0.2,
- type: 'trace',
- condition: {
- op: 'and',
- inner: [
- {
- op: 'glob',
- name: 'trace.release',
- value: ['1.2.2'],
- },
- ],
- },
- id: 40,
- },
- {
- sampleRate: 0.5,
- type: 'trace',
- condition: {
- op: 'and',
- inner: [],
- },
- id: 41,
- },
- ],
- next_id: 42,
- },
- }),
- });
- renderComponent();
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Empty conditions message
- expect(screen.getByText('No conditions added')).toBeInTheDocument();
- // A hint about an existing 'sample all' rule is displayed
- expect(
- screen.getByText(
- 'A rule with no conditions already exists. You can edit that existing rule or add a condition to this rule'
- )
- ).toBeInTheDocument();
- // Adds a sample rate
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '5');
- // Button is still disabled
- expect(screen.getByLabelText('Save Rule')).toBeDisabled();
- });
- it('does not let you save a individual transaction rule without a condition, if a transaction rule without a condition already exists', async function () {
- MockApiClient.addMockResponse({
- url: '/projects/org-slug/project-slug/',
- method: 'GET',
- body: TestStubs.Project({
- dynamicSampling: {
- rules: [
- {
- condition: {
- inner: [{name: 'event.release', op: 'glob', value: ['1.2.3']}],
- op: 'and',
- },
- id: 0,
- sampleRate: 0.3,
- type: 'transaction',
- },
- {
- condition: {
- inner: [],
- op: 'and',
- },
- id: 1,
- sampleRate: 0.2,
- type: 'transaction',
- },
- ],
- next_id: 2,
- },
- }),
- });
- renderComponent({ruleType: SamplingRuleType.TRANSACTION});
- // Open Modal
- await openSamplingRuleModal(screen.getByText('Add Rule'));
- // Empty conditions message
- expect(screen.getByText('No conditions added')).toBeInTheDocument();
- // A hint about an existing 'sample all' rule is displayed
- expect(
- screen.getByText(
- 'A rule with no conditions already exists. You can edit that existing rule or add a condition to this rule'
- )
- ).toBeInTheDocument();
- // Adds a sample rate
- userEvent.paste(screen.getByPlaceholderText('\u0025'), '5');
- // Button is still disabled
- expect(screen.getByLabelText('Save Rule')).toBeDisabled();
- });
- });
|