import selectEvent from 'react-select-event'; import {ProjectFixture} from 'sentry-fixture/project'; import {TeamFixture} from 'sentry-fixture/team'; import {UserFixture} from 'sentry-fixture/user'; import {initializeOrg} from 'sentry-test/initializeOrg'; import { render, screen, userEvent, waitFor, waitForElementToBeRemoved, } from 'sentry-test/reactTestingLibrary'; import MemberListStore from 'sentry/stores/memberListStore'; import ProjectsStore from 'sentry/stores/projectsStore'; import TeamStore from 'sentry/stores/teamStore'; import type {Project} from 'sentry/types'; import RuleBuilder from 'sentry/views/settings/project/projectOwnership/ruleBuilder'; describe('RuleBuilder', function () { const {organization} = initializeOrg(); let project: Project; let handleAdd: jest.Mock; const USER_1 = UserFixture({ id: '1', name: 'Jane Bloggs', email: 'janebloggs@example.com', }); const USER_2 = UserFixture({ id: '2', name: 'John Smith', email: 'johnsmith@example.com', }); const TEAM_1 = TeamFixture({ id: '3', slug: 'cool-team', }); // This team is in project const TEAM_2 = TeamFixture({ id: '4', slug: 'team-not-in-project', }); beforeEach(function () { // User in project MemberListStore.loadInitialData([USER_1]); // All teams jest.spyOn(TeamStore, 'getAll').mockImplementation(() => [TEAM_1, TEAM_2]); handleAdd = jest.fn(); project = ProjectFixture({ // Teams in project teams: [TEAM_1], }); ProjectsStore.loadInitialData([project]); jest.spyOn(ProjectsStore, 'getBySlug').mockImplementation(() => project); MockApiClient.clearMockResponses(); MockApiClient.addMockResponse({ url: '/organizations/org-slug/members/', body: [ {...USER_1, user: USER_1}, {...USER_2, user: USER_2}, ], }); }); it('renders', async function () { render( ); const addButton = screen.getByRole('button', {name: 'Add rule'}); await userEvent.click(addButton); expect(handleAdd).not.toHaveBeenCalled(); await userEvent.type( screen.getByRole('textbox', {name: 'Rule pattern'}), 'some/path/*' ); expect(addButton).toBeDisabled(); await selectEvent.select( screen.getByRole('textbox', {name: 'Rule owner'}), 'Jane Bloggs' ); expect(addButton).toBeEnabled(); await userEvent.click(addButton); expect(handleAdd).toHaveBeenCalled(); }); it('renders with suggestions', async function () { render( ); // Open the menu so we can do some assertions. const ownerInput = screen.getByRole('textbox', {name: 'Rule owner'}); selectEvent.openMenu(ownerInput); await waitForElementToBeRemoved(() => screen.queryByText('Loading...')); expect(screen.getByText('Jane Bloggs')).toBeInTheDocument(); expect(screen.getByText('John Smith')).toBeInTheDocument(); expect(screen.getByText('#cool-team')).toBeInTheDocument(); expect(screen.getByText('#team-not-in-project')).toBeInTheDocument(); // TODO Check that the last two are disabled // Enter to select Jane Bloggs await selectEvent.select(ownerInput, 'Jane Bloggs'); const candidates = screen.getAllByRole('button', {name: 'Path rule candidate'}); await userEvent.click(candidates[0]); expect(screen.getByRole('textbox', {name: 'Rule pattern'})).toHaveValue('a/bar'); const addButton = screen.getByRole('button', {name: 'Add rule'}); await waitFor(() => expect(addButton).toBeEnabled()); await userEvent.click(addButton); expect(handleAdd).toHaveBeenCalled(); }); it('builds a tag rule', async function () { render( ); await selectEvent.select(screen.getByText('Path'), 'Tag'); await userEvent.type(screen.getByPlaceholderText('tag-name'), 'mytag'); await userEvent.type(screen.getByPlaceholderText('tag-value'), 'value'); await selectEvent.select( screen.getByRole('textbox', {name: 'Rule owner'}), 'Jane Bloggs' ); await userEvent.click(screen.getByRole('button', {name: 'Add rule'})); expect(handleAdd).toHaveBeenCalledWith('tags.mytag:value janebloggs@example.com'); }); });