ownershipRulesTable.spec.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import ConfigStore from 'sentry/stores/configStore';
  3. import MemberListStore from 'sentry/stores/memberListStore';
  4. import type {Actor, ParsedOwnershipRule} from 'sentry/types';
  5. import {OwnershipRulesTable} from './ownershipRulesTable';
  6. describe('OwnershipRulesTable', () => {
  7. const user1 = TestStubs.User();
  8. const user2 = TestStubs.User({id: '2', name: 'Jane Doe'});
  9. beforeEach(() => {
  10. ConfigStore.init();
  11. ConfigStore.set('user', user1);
  12. MemberListStore.init();
  13. MemberListStore.loadInitialData([user1, user2]);
  14. });
  15. it('should render empty state', () => {
  16. render(<OwnershipRulesTable projectRules={[]} codeowners={[]} />);
  17. expect(screen.getByText('No ownership rules found')).toBeInTheDocument();
  18. });
  19. it('should render project owners members', () => {
  20. const rules: ParsedOwnershipRule[] = [
  21. {
  22. matcher: {pattern: 'pattern', type: 'path'},
  23. owners: [{type: 'user', id: user1.id, name: user1.name}],
  24. },
  25. ];
  26. render(<OwnershipRulesTable projectRules={rules} codeowners={[]} />);
  27. expect(screen.getByText('path')).toBeInTheDocument();
  28. expect(screen.getByText('pattern')).toBeInTheDocument();
  29. expect(screen.getByText(user1.name)).toBeInTheDocument();
  30. });
  31. it('should filter codeowners rules without actor names', () => {
  32. const rules: ParsedOwnershipRule[] = [
  33. {
  34. matcher: {pattern: 'pattern', type: 'path'},
  35. // Name = undefined only seems to happen when adding a new codeowners file
  36. owners: [{type: 'user', id: user1.id, name: undefined as any}],
  37. },
  38. {
  39. matcher: {pattern: 'my/path', type: 'path'},
  40. owners: [{type: 'user', id: user2.id, name: user2.name}],
  41. },
  42. ];
  43. render(
  44. <OwnershipRulesTable
  45. projectRules={[]}
  46. codeowners={[TestStubs.CodeOwner({schema: {rules}})]}
  47. />
  48. );
  49. expect(screen.getByText('pattern')).toBeInTheDocument();
  50. expect(screen.getByText('my/path')).toBeInTheDocument();
  51. expect(screen.getByRole('button', {name: 'Everyone'})).toBeEnabled();
  52. });
  53. it('should render multiple project owners', () => {
  54. const rules: ParsedOwnershipRule[] = [
  55. {
  56. matcher: {pattern: 'pattern', type: 'path'},
  57. owners: [
  58. {type: 'user', id: user1.id, name: user1.name},
  59. {type: 'user', id: user2.id, name: user2.name},
  60. ],
  61. },
  62. ];
  63. render(<OwnershipRulesTable projectRules={rules} codeowners={[]} />);
  64. expect(screen.getByText('path')).toBeInTheDocument();
  65. expect(screen.getByText('pattern')).toBeInTheDocument();
  66. expect(screen.getByText(`${user1.name} and 1 other`)).toBeInTheDocument();
  67. expect(screen.queryByText(user2.name)).not.toBeInTheDocument();
  68. });
  69. it('should filter by rule type and pattern', async () => {
  70. const owners: Actor[] = [{type: 'user', id: user1.id, name: user1.name}];
  71. const rules: ParsedOwnershipRule[] = [
  72. {matcher: {pattern: 'filepath', type: 'path'}, owners},
  73. {matcher: {pattern: 'mytag', type: 'tag'}, owners},
  74. ];
  75. render(<OwnershipRulesTable projectRules={rules} codeowners={[]} />);
  76. const searchbar = screen.getByPlaceholderText('Search by type or rule');
  77. await userEvent.click(searchbar);
  78. await userEvent.paste('path');
  79. expect(screen.getByText('filepath')).toBeInTheDocument();
  80. expect(screen.queryByText('mytag')).not.toBeInTheDocument();
  81. // Change the filter to mytag
  82. await userEvent.clear(searchbar);
  83. await userEvent.paste('mytag');
  84. expect(screen.getByText('mytag')).toBeInTheDocument();
  85. expect(screen.queryByText('filepath')).not.toBeInTheDocument();
  86. });
  87. it('should filter by my teams by default', async () => {
  88. const rules: ParsedOwnershipRule[] = [
  89. {
  90. matcher: {pattern: 'filepath', type: 'path'},
  91. owners: [{type: 'user', id: user1.id, name: user1.name}],
  92. },
  93. {
  94. matcher: {pattern: 'mytag', type: 'tag'},
  95. owners: [{type: 'user', id: user2.id, name: user2.name}],
  96. },
  97. ];
  98. render(<OwnershipRulesTable projectRules={rules} codeowners={[]} />);
  99. expect(screen.getByText('filepath')).toBeInTheDocument();
  100. expect(screen.queryByText('mytag')).not.toBeInTheDocument();
  101. // Clear the filter
  102. await userEvent.click(screen.getByRole('button', {name: 'My Teams'}));
  103. await userEvent.click(screen.getByRole('button', {name: 'Clear'}));
  104. expect(screen.getByText('filepath')).toBeInTheDocument();
  105. expect(screen.queryByText('mytag')).toBeInTheDocument();
  106. });
  107. it('preserves selected teams when rules are updated', async () => {
  108. const rules: ParsedOwnershipRule[] = [
  109. {
  110. matcher: {pattern: 'filepath', type: 'path'},
  111. owners: [{type: 'user', id: user1.id, name: user1.name}],
  112. },
  113. {
  114. matcher: {pattern: 'anotherpath', type: 'path'},
  115. owners: [{type: 'user', id: user2.id, name: user2.name}],
  116. },
  117. ];
  118. const {rerender} = render(
  119. <OwnershipRulesTable projectRules={rules} codeowners={[]} />
  120. );
  121. // Clear the filter
  122. await userEvent.click(screen.getByRole('button', {name: 'My Teams'}));
  123. await userEvent.click(screen.getByRole('button', {name: 'Clear'}));
  124. expect(screen.getAllByText('path')).toHaveLength(2);
  125. const newRules: ParsedOwnershipRule[] = [
  126. ...rules,
  127. {
  128. matcher: {pattern: 'thirdpath', type: 'path'},
  129. owners: [{type: 'user', id: user2.id, name: user2.name}],
  130. },
  131. ];
  132. rerender(<OwnershipRulesTable projectRules={newRules} codeowners={[]} />);
  133. expect(screen.getAllByText('path')).toHaveLength(3);
  134. expect(screen.getByRole('button', {name: 'Everyone'})).toBeInTheDocument();
  135. });
  136. it('should paginate results', async () => {
  137. const owners: Actor[] = [{type: 'user', id: user1.id, name: user1.name}];
  138. const rules: ParsedOwnershipRule[] = Array(100)
  139. .fill(0)
  140. .map((_, i) => ({
  141. matcher: {pattern: `mytag${i}`, type: 'tag'},
  142. owners,
  143. }));
  144. render(<OwnershipRulesTable projectRules={rules} codeowners={[]} />);
  145. expect(screen.getByText('mytag1')).toBeInTheDocument();
  146. await userEvent.click(screen.getByRole('button', {name: 'Next page'}));
  147. expect(screen.getByText('mytag30')).toBeInTheDocument();
  148. expect(screen.queryByText('mytag1')).not.toBeInTheDocument();
  149. });
  150. });