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');
});
});