import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import {TeamFixture} from 'sentry-fixture/team'; import {UserFixture} from 'sentry-fixture/user'; import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary'; import ProjectsStore from 'sentry/stores/projectsStore'; import TeamStore from 'sentry/stores/teamStore'; import EventView from 'sentry/utils/discover/eventView'; import {MAX_TEAM_KEY_TRANSACTIONS} from 'sentry/utils/performance/constants'; import TeamKeyTransactionButton from 'sentry/views/performance/transactionSummary/teamKeyTransactionButton'; async function clickTeamKeyTransactionDropdown() { await waitFor(() => expect(screen.getByRole('button', {expanded: false})).toBeEnabled() ); await userEvent.click(screen.getByRole('button', {expanded: false})); } describe('TeamKeyTransactionButton', function () { const organization = OrganizationFixture({features: ['performance-view']}); const teams = [ TeamFixture({id: '1', slug: 'team1', name: 'Team 1'}), TeamFixture({id: '2', slug: 'team2', name: 'Team 2'}), ]; const project = ProjectFixture({teams}); const eventView = new EventView({ id: '1', name: 'my query', fields: [{field: 'count()'}], sorts: [{field: 'count', kind: 'desc'}], query: '', project: [parseInt(project.id, 10)], start: '2019-10-01T00:00:00', end: '2019-10-02T00:00:00', statsPeriod: '14d', environment: [], createdBy: UserFixture(), display: 'line', team: ['myteams'], topEvents: '5', }); beforeEach(function () { MockApiClient.clearMockResponses(); act(() => ProjectsStore.loadInitialData([project])); act(() => void TeamStore.loadInitialData(teams, false, null)); }); it('fetches key transactions with project param', function () { const getTeamKeyTransactionsMock = MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 1, keyed: [{project_id: String(project.id), transaction: 'transaction'}], })), match: [MockApiClient.matchQuery({project: [project.id], team: ['myteams']})], }); render( ); expect(getTeamKeyTransactionsMock).toHaveBeenCalledTimes(1); }); it('renders with all teams checked', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 1, keyed: [{project_id: String(project.id), transaction: 'transaction'}], })), }); render( ); await clickTeamKeyTransactionDropdown(); // all teams should be checked expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); }); it('renders with some teams checked', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: id === teams[0].id ? 1 : 0, keyed: id === teams[0].id ? [{project_id: String(project.id), transaction: 'transaction'}] : [], })), }); render( ); await clickTeamKeyTransactionDropdown(); // only team 1 should be checked expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'false' ); }); it('renders with no teams checked', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 0, keyed: [], })), }); render( ); await clickTeamKeyTransactionDropdown(); // all teams should be unchecked expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'false' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'false' ); }); it('should be able to check one team', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 0, keyed: [], })), }); const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({ method: 'POST', url: '/organizations/org-slug/key-transactions/', body: [], match: [ MockApiClient.matchQuery({project: [project.id]}), MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}), ], }); render( ); await clickTeamKeyTransactionDropdown(); await userEvent.click(screen.getByRole('option', {name: `#${teams[0].slug}`})); expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1); }); it('should be able to uncheck one team', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 1, keyed: [{project_id: String(project.id), transaction: 'transaction'}], })), }); const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({ method: 'DELETE', url: '/organizations/org-slug/key-transactions/', body: [], match: [ MockApiClient.matchQuery({project: [project.id]}), MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}), ], }); render( ); await clickTeamKeyTransactionDropdown(); await userEvent.click(screen.getByRole('option', {name: `#${teams[0].slug}`})); expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1); }); it('should be able to check all with my teams', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 0, keyed: [], })), }); const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({ method: 'POST', url: '/organizations/org-slug/key-transactions/', body: [], match: [ MockApiClient.matchQuery({project: [project.id]}), MockApiClient.matchData({ team: [teams[0].id, teams[1].id], transaction: 'transaction', }), ], }); render( ); await clickTeamKeyTransactionDropdown(); await userEvent.click(screen.getByRole('button', {name: 'Select All in My Teams'})); // all teams should be checked now expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1); }); it('should be able to uncheck all with my teams', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: 1, keyed: [{project_id: String(project.id), transaction: 'transaction'}], })), }); const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({ method: 'DELETE', url: '/organizations/org-slug/key-transactions/', body: [], match: [ MockApiClient.matchQuery({project: [project.id]}), MockApiClient.matchData({ team: [teams[0].id, teams[1].id], transaction: 'transaction', }), ], }); render( ); await clickTeamKeyTransactionDropdown(); await userEvent.click(screen.getByRole('button', {name: 'Unselect All in My Teams'})); // all teams should be checked now expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'false' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'false' ); expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1); }); it('renders unkeyed as disabled if count exceeds max', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: MAX_TEAM_KEY_TRANSACTIONS, keyed: Array.from({length: MAX_TEAM_KEY_TRANSACTIONS}, (_, i) => ({ project_id: String(project.id), transaction: `transaction-${i}`, })), })), }); render( ); await clickTeamKeyTransactionDropdown(); expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-disabled', 'true' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-disabled', 'true' ); }); it('renders keyed as checked even if count is maxed', async function () { MockApiClient.addMockResponse({ method: 'GET', url: '/organizations/org-slug/key-transactions-list/', body: teams.map(({id}) => ({ team: id, count: MAX_TEAM_KEY_TRANSACTIONS, keyed: [ {project_id: String(project.id), transaction: 'transaction'}, ...Array.from({length: MAX_TEAM_KEY_TRANSACTIONS - 1}, (_, i) => ({ project_id: String(project.id), transaction: `transaction-${i}`, })), ], })), }); render( ); await clickTeamKeyTransactionDropdown(); expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute( 'aria-selected', 'true' ); }); });