ruleBuilder.spec.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import selectEvent from 'react-select-event';
  2. import {ProjectFixture} from 'sentry-fixture/project';
  3. import {TeamFixture} from 'sentry-fixture/team';
  4. import {UserFixture} from 'sentry-fixture/user';
  5. import {initializeOrg} from 'sentry-test/initializeOrg';
  6. import {
  7. render,
  8. screen,
  9. userEvent,
  10. waitFor,
  11. waitForElementToBeRemoved,
  12. } from 'sentry-test/reactTestingLibrary';
  13. import MemberListStore from 'sentry/stores/memberListStore';
  14. import ProjectsStore from 'sentry/stores/projectsStore';
  15. import TeamStore from 'sentry/stores/teamStore';
  16. import type {Project} from 'sentry/types';
  17. import RuleBuilder from 'sentry/views/settings/project/projectOwnership/ruleBuilder';
  18. describe('RuleBuilder', function () {
  19. const {organization} = initializeOrg();
  20. let project: Project;
  21. let handleAdd: jest.Mock;
  22. const USER_1 = UserFixture({
  23. id: '1',
  24. name: 'Jane Bloggs',
  25. email: 'janebloggs@example.com',
  26. });
  27. const USER_2 = UserFixture({
  28. id: '2',
  29. name: 'John Smith',
  30. email: 'johnsmith@example.com',
  31. });
  32. const TEAM_1 = TeamFixture({
  33. id: '3',
  34. slug: 'cool-team',
  35. });
  36. // This team is in project
  37. const TEAM_2 = TeamFixture({
  38. id: '4',
  39. slug: 'team-not-in-project',
  40. });
  41. beforeEach(function () {
  42. // User in project
  43. MemberListStore.loadInitialData([USER_1]);
  44. // All teams
  45. jest.spyOn(TeamStore, 'getAll').mockImplementation(() => [TEAM_1, TEAM_2]);
  46. handleAdd = jest.fn();
  47. project = ProjectFixture({
  48. // Teams in project
  49. teams: [TEAM_1],
  50. });
  51. ProjectsStore.loadInitialData([project]);
  52. jest.spyOn(ProjectsStore, 'getBySlug').mockImplementation(() => project);
  53. MockApiClient.clearMockResponses();
  54. MockApiClient.addMockResponse({
  55. url: '/organizations/org-slug/members/',
  56. body: [
  57. {...USER_1, user: USER_1},
  58. {...USER_2, user: USER_2},
  59. ],
  60. });
  61. });
  62. it('renders', async function () {
  63. render(
  64. <RuleBuilder
  65. project={project}
  66. organization={organization}
  67. onAddRule={handleAdd}
  68. paths={[]}
  69. urls={[]}
  70. disabled={false}
  71. />
  72. );
  73. const addButton = screen.getByRole('button', {name: 'Add rule'});
  74. await userEvent.click(addButton);
  75. expect(handleAdd).not.toHaveBeenCalled();
  76. await userEvent.type(
  77. screen.getByRole('textbox', {name: 'Rule pattern'}),
  78. 'some/path/*'
  79. );
  80. expect(addButton).toBeDisabled();
  81. await selectEvent.select(
  82. screen.getByRole('textbox', {name: 'Rule owner'}),
  83. 'Jane Bloggs'
  84. );
  85. expect(addButton).toBeEnabled();
  86. await userEvent.click(addButton);
  87. expect(handleAdd).toHaveBeenCalled();
  88. });
  89. it('renders with suggestions', async function () {
  90. render(
  91. <RuleBuilder
  92. project={project}
  93. organization={organization}
  94. onAddRule={handleAdd}
  95. urls={['example.com/a', 'example.com/a/foo']}
  96. paths={['a/bar', 'a/foo']}
  97. disabled={false}
  98. />
  99. );
  100. // Open the menu so we can do some assertions.
  101. const ownerInput = screen.getByRole('textbox', {name: 'Rule owner'});
  102. selectEvent.openMenu(ownerInput);
  103. await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));
  104. expect(screen.getByText('Jane Bloggs')).toBeInTheDocument();
  105. expect(screen.getByText('John Smith')).toBeInTheDocument();
  106. expect(screen.getByText('#cool-team')).toBeInTheDocument();
  107. expect(screen.getByText('#team-not-in-project')).toBeInTheDocument();
  108. // TODO Check that the last two are disabled
  109. // Enter to select Jane Bloggs
  110. await selectEvent.select(ownerInput, 'Jane Bloggs');
  111. const candidates = screen.getAllByRole('button', {name: 'Path rule candidate'});
  112. await userEvent.click(candidates[0]);
  113. expect(screen.getByRole('textbox', {name: 'Rule pattern'})).toHaveValue('a/bar');
  114. const addButton = screen.getByRole('button', {name: 'Add rule'});
  115. await waitFor(() => expect(addButton).toBeEnabled());
  116. await userEvent.click(addButton);
  117. expect(handleAdd).toHaveBeenCalled();
  118. });
  119. it('builds a tag rule', async function () {
  120. render(
  121. <RuleBuilder
  122. project={project}
  123. organization={organization}
  124. onAddRule={handleAdd}
  125. paths={[]}
  126. urls={[]}
  127. disabled={false}
  128. />
  129. );
  130. await selectEvent.select(screen.getByText('Path'), 'Tag');
  131. await userEvent.type(screen.getByPlaceholderText('tag-name'), 'mytag');
  132. await userEvent.type(screen.getByPlaceholderText('tag-value'), 'value');
  133. await selectEvent.select(
  134. screen.getByRole('textbox', {name: 'Rule owner'}),
  135. 'Jane Bloggs'
  136. );
  137. await userEvent.click(screen.getByRole('button', {name: 'Add rule'}));
  138. expect(handleAdd).toHaveBeenCalledWith('tags.mytag:value janebloggs@example.com');
  139. });
  140. });