inviteBanner.spec.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import moment from 'moment';
  2. import {MemberFixture} from 'sentry-fixture/member';
  3. import {MissingMembersFixture} from 'sentry-fixture/missingMembers';
  4. import {OrganizationFixture} from 'sentry-fixture/organization';
  5. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  6. import {DEFAULT_SNOOZE_PROMPT_DAYS} from 'sentry/utils/promptIsDismissed';
  7. import {InviteBanner} from 'sentry/views/settings/organizationMembers/inviteBanner';
  8. const missingMembers = {
  9. integration: 'github',
  10. users: MissingMembersFixture(),
  11. };
  12. const noMissingMembers = {
  13. integration: 'github',
  14. users: [],
  15. };
  16. describe('inviteBanner', function () {
  17. beforeEach(function () {
  18. MockApiClient.clearMockResponses();
  19. MockApiClient.addMockResponse({
  20. url: '/organizations/org-slug/missing-members/',
  21. method: 'GET',
  22. body: [missingMembers],
  23. });
  24. MockApiClient.addMockResponse({
  25. url: '/organizations/org-slug/prompts-activity/',
  26. method: 'GET',
  27. body: {
  28. dismissed_ts: undefined,
  29. snoozed_ts: undefined,
  30. },
  31. });
  32. });
  33. it('render banners', async function () {
  34. const org = OrganizationFixture({
  35. githubNudgeInvite: true,
  36. });
  37. render(
  38. <InviteBanner
  39. onSendInvite={() => {}}
  40. organization={org}
  41. allowedRoles={[]}
  42. onModalClose={() => {}}
  43. />
  44. );
  45. expect(
  46. await screen.findByRole('heading', {
  47. name: 'Bring your full GitHub team on board in Sentry',
  48. })
  49. ).toBeInTheDocument();
  50. expect(screen.queryAllByTestId('invite-missing-member')).toHaveLength(5);
  51. expect(screen.getByText('See all 5 missing members')).toBeInTheDocument();
  52. });
  53. it('does not render banner if no option', function () {
  54. const org = OrganizationFixture({});
  55. const {container} = render(
  56. <InviteBanner
  57. onSendInvite={() => {}}
  58. organization={org}
  59. allowedRoles={[]}
  60. onModalClose={() => {}}
  61. />
  62. );
  63. expect(container).toBeEmptyDOMElement();
  64. });
  65. it('does not render banner if no missing members', async function () {
  66. const org = OrganizationFixture({
  67. githubNudgeInvite: true,
  68. });
  69. const mock = MockApiClient.addMockResponse({
  70. url: '/organizations/org-slug/missing-members/',
  71. method: 'GET',
  72. body: [noMissingMembers],
  73. });
  74. const {container} = render(
  75. <InviteBanner
  76. onSendInvite={() => {}}
  77. organization={org}
  78. allowedRoles={[]}
  79. onModalClose={() => {}}
  80. />
  81. );
  82. await waitFor(() => expect(mock).toHaveBeenCalledTimes(1));
  83. expect(container).toBeEmptyDOMElement();
  84. });
  85. it('does not render banner if no integration', async function () {
  86. const org = OrganizationFixture({
  87. githubNudgeInvite: true,
  88. });
  89. const mock = MockApiClient.addMockResponse({
  90. url: '/organizations/org-slug/missing-members/',
  91. method: 'GET',
  92. body: [],
  93. });
  94. const {container} = render(
  95. <InviteBanner
  96. onSendInvite={() => {}}
  97. organization={org}
  98. allowedRoles={[]}
  99. onModalClose={() => {}}
  100. />
  101. );
  102. await waitFor(() => expect(mock).toHaveBeenCalledTimes(1));
  103. expect(container).toBeEmptyDOMElement();
  104. });
  105. it('does not render banner if lacking org:write', function () {
  106. const org = OrganizationFixture({
  107. access: [],
  108. githubNudgeInvite: true,
  109. });
  110. const {container} = render(
  111. <InviteBanner
  112. onSendInvite={() => {}}
  113. organization={org}
  114. allowedRoles={[]}
  115. onModalClose={() => {}}
  116. />
  117. );
  118. expect(container).toBeEmptyDOMElement();
  119. });
  120. it('renders banner if snoozed_ts days is longer than threshold', async function () {
  121. const org = OrganizationFixture({
  122. githubNudgeInvite: true,
  123. });
  124. const promptResponse = {
  125. dismissed_ts: undefined,
  126. snoozed_ts: moment
  127. .utc()
  128. .subtract(DEFAULT_SNOOZE_PROMPT_DAYS + 1, 'days')
  129. .unix(),
  130. };
  131. MockApiClient.addMockResponse({
  132. url: `/organizations/${org.slug}/prompts-activity/`,
  133. method: 'GET',
  134. body: {data: promptResponse},
  135. });
  136. render(
  137. <InviteBanner
  138. onSendInvite={() => {}}
  139. organization={org}
  140. allowedRoles={[]}
  141. onModalClose={() => {}}
  142. />
  143. );
  144. expect(
  145. await screen.findByRole('heading', {
  146. name: 'Bring your full GitHub team on board in Sentry',
  147. })
  148. ).toBeInTheDocument();
  149. });
  150. it('does not render banner if snoozed_ts days is shorter than threshold', async function () {
  151. const org = OrganizationFixture({
  152. githubNudgeInvite: true,
  153. });
  154. const promptResponse = {
  155. dismissed_ts: undefined,
  156. snoozed_ts: moment
  157. .utc()
  158. .subtract(DEFAULT_SNOOZE_PROMPT_DAYS - 1, 'days')
  159. .unix(),
  160. };
  161. const mockPrompt = MockApiClient.addMockResponse({
  162. url: `/organizations/${org.slug}/prompts-activity/`,
  163. method: 'GET',
  164. body: {data: promptResponse},
  165. });
  166. const {container} = render(
  167. <InviteBanner
  168. onSendInvite={() => {}}
  169. organization={org}
  170. allowedRoles={[]}
  171. onModalClose={() => {}}
  172. />
  173. );
  174. await waitFor(() => expect(mockPrompt).toHaveBeenCalled());
  175. expect(container).toBeEmptyDOMElement();
  176. });
  177. it('invites member from banner', async function () {
  178. const newMember = MemberFixture({
  179. id: '6',
  180. email: 'hello@sentry.io',
  181. teams: [],
  182. teamRoles: [],
  183. flags: {
  184. 'idp:provisioned': false,
  185. 'idp:role-restricted': false,
  186. 'member-limit:restricted': false,
  187. 'partnership:restricted': false,
  188. 'sso:invalid': false,
  189. 'sso:linked': true,
  190. },
  191. });
  192. MockApiClient.addMockResponse({
  193. url: '/organizations/org-slug/missing-members/',
  194. method: 'GET',
  195. body: [
  196. {
  197. integration: 'github',
  198. users: MissingMembersFixture().slice(0, 5),
  199. },
  200. ],
  201. });
  202. MockApiClient.addMockResponse({
  203. url: '/organizations/org-slug/members/?referrer=github_nudge_invite',
  204. method: 'POST',
  205. body: newMember,
  206. });
  207. const org = OrganizationFixture({
  208. githubNudgeInvite: true,
  209. });
  210. render(
  211. <InviteBanner
  212. onSendInvite={() => {}}
  213. organization={org}
  214. allowedRoles={[]}
  215. onModalClose={() => {}}
  216. />
  217. );
  218. expect(
  219. await screen.findByRole('heading', {
  220. name: 'Bring your full GitHub team on board in Sentry',
  221. })
  222. ).toBeInTheDocument();
  223. expect(screen.queryAllByTestId('invite-missing-member')).toHaveLength(5);
  224. expect(screen.getByText('See all 5 missing members')).toBeInTheDocument();
  225. const inviteButton = screen.queryAllByTestId('invite-missing-member')[0];
  226. await userEvent.click(inviteButton);
  227. expect(screen.queryAllByTestId('invite-missing-member')).toHaveLength(4);
  228. expect(screen.getByText('See all 4 missing members')).toBeInTheDocument();
  229. });
  230. });