ruleBuilder.spec.tsx 4.7 KB

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