ruleBuilder.spec.tsx 4.7 KB

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