organizationMemberDetail.spec.jsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. import selectEvent from 'react-select-event';
  2. import {
  3. cleanup,
  4. render,
  5. renderGlobalModal,
  6. screen,
  7. userEvent,
  8. within,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import {updateMember} from 'sentry/actionCreators/members';
  11. import TeamStore from 'sentry/stores/teamStore';
  12. import OrganizationMemberDetail from 'sentry/views/settings/organizationMembers/organizationMemberDetail';
  13. jest.mock('sentry/actionCreators/members', () => ({
  14. updateMember: jest.fn().mockReturnValue(new Promise(() => {})),
  15. }));
  16. describe('OrganizationMemberDetail', function () {
  17. let organization;
  18. let routerContext;
  19. const team = TestStubs.Team();
  20. const idpTeam = TestStubs.Team({
  21. id: '4',
  22. slug: 'idp-member-team',
  23. name: 'Idp Member Team',
  24. isMember: true,
  25. flags: {
  26. 'idp:provisioned': true,
  27. },
  28. });
  29. const managerTeam = TestStubs.Team({id: '5', orgRole: 'manager', slug: 'manager-team'});
  30. const teams = [
  31. team,
  32. TestStubs.Team({
  33. id: '2',
  34. slug: 'new-team',
  35. name: 'New Team',
  36. isMember: false,
  37. }),
  38. TestStubs.Team({
  39. id: '3',
  40. slug: 'idp-team',
  41. name: 'Idp Team',
  42. isMember: false,
  43. flags: {
  44. 'idp:provisioned': true,
  45. },
  46. }),
  47. idpTeam,
  48. managerTeam,
  49. ];
  50. const teamAssignment = {
  51. teams: [team.slug],
  52. teamRoles: [
  53. {
  54. teamSlug: team.slug,
  55. role: null,
  56. },
  57. ],
  58. };
  59. const member = TestStubs.Member({
  60. roles: TestStubs.OrgRoleList(),
  61. dateCreated: new Date(),
  62. ...teamAssignment,
  63. });
  64. const pendingMember = TestStubs.Member({
  65. id: 2,
  66. roles: TestStubs.OrgRoleList(),
  67. dateCreated: new Date(),
  68. ...teamAssignment,
  69. invite_link: 'http://example.com/i/abc123',
  70. pending: true,
  71. });
  72. const expiredMember = TestStubs.Member({
  73. id: 3,
  74. roles: TestStubs.OrgRoleList(),
  75. dateCreated: new Date(),
  76. ...teamAssignment,
  77. invite_link: 'http://example.com/i/abc123',
  78. pending: true,
  79. expired: true,
  80. });
  81. const idpTeamMember = TestStubs.Member({
  82. id: 4,
  83. roles: TestStubs.OrgRoleList(),
  84. dateCreated: new Date(),
  85. teams: [idpTeam.slug],
  86. teamRoles: [
  87. {
  88. teamSlug: idpTeam.slug,
  89. role: null,
  90. },
  91. ],
  92. });
  93. beforeAll(() => {
  94. TeamStore.loadInitialData(teams);
  95. });
  96. describe('Can Edit', function () {
  97. beforeEach(function () {
  98. organization = TestStubs.Organization({teams, features: ['team-roles']});
  99. routerContext = TestStubs.routerContext([{organization}]);
  100. TeamStore.init();
  101. TeamStore.loadInitialData(teams);
  102. jest.resetAllMocks();
  103. MockApiClient.clearMockResponses();
  104. MockApiClient.addMockResponse({
  105. url: `/organizations/${organization.slug}/members/${member.id}/`,
  106. body: member,
  107. });
  108. MockApiClient.addMockResponse({
  109. url: `/organizations/${organization.slug}/members/${pendingMember.id}/`,
  110. body: pendingMember,
  111. });
  112. MockApiClient.addMockResponse({
  113. url: `/organizations/${organization.slug}/members/${expiredMember.id}/`,
  114. body: expiredMember,
  115. });
  116. MockApiClient.addMockResponse({
  117. url: `/organizations/${organization.slug}/members/${idpTeamMember.id}/`,
  118. body: idpTeamMember,
  119. });
  120. MockApiClient.addMockResponse({
  121. url: `/organizations/${organization.slug}/teams/`,
  122. body: teams,
  123. });
  124. });
  125. it('changes org role to owner', async function () {
  126. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  127. context: routerContext,
  128. });
  129. // Should have 4 roles
  130. const radios = screen.getAllByRole('radio');
  131. expect(radios).toHaveLength(4);
  132. // Click last radio
  133. await userEvent.click(radios.at(-1));
  134. expect(radios.at(-1)).toBeChecked();
  135. // Save Member
  136. await userEvent.click(screen.getByRole('button', {name: 'Save Member'}));
  137. expect(updateMember).toHaveBeenCalledWith(
  138. expect.anything(),
  139. expect.objectContaining({
  140. data: expect.objectContaining({
  141. orgRole: 'owner',
  142. }),
  143. })
  144. );
  145. });
  146. it('leaves a team', async function () {
  147. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  148. context: routerContext,
  149. });
  150. // Remove our one team
  151. await userEvent.click(screen.getByRole('button', {name: 'Remove'}));
  152. // Save Member
  153. await userEvent.click(screen.getByRole('button', {name: 'Save Member'}));
  154. expect(updateMember).toHaveBeenCalledWith(
  155. expect.anything(),
  156. expect.objectContaining({
  157. data: expect.objectContaining({
  158. teamRoles: [],
  159. }),
  160. })
  161. );
  162. });
  163. it('cannot leave idp-provisioned team', function () {
  164. render(<OrganizationMemberDetail params={{memberId: idpTeamMember.id}} />, {
  165. context: routerContext,
  166. });
  167. expect(screen.getByRole('button', {name: 'Remove'})).toBeDisabled();
  168. });
  169. it('joins a team and assign a team-role', async function () {
  170. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  171. context: routerContext,
  172. });
  173. // Should have one team enabled
  174. expect(screen.getByTestId('team-row-for-member')).toBeInTheDocument();
  175. // Select new team to join
  176. // Open the dropdown
  177. await userEvent.click(screen.getByText('Add Team'));
  178. // Click the first item
  179. await userEvent.click(screen.getByText('#new-team'));
  180. // Assign as admin to new team
  181. const teamRoleSelect = screen.getAllByText('Contributor')[1];
  182. await selectEvent.select(teamRoleSelect, ['Team Admin']);
  183. // Save Member
  184. await userEvent.click(screen.getByRole('button', {name: 'Save Member'}));
  185. expect(updateMember).toHaveBeenCalledWith(
  186. expect.anything(),
  187. expect.objectContaining({
  188. data: expect.objectContaining({
  189. teamRoles: [
  190. {teamSlug: 'team-slug', role: null},
  191. {teamSlug: 'new-team', role: 'admin'},
  192. ],
  193. }),
  194. })
  195. );
  196. });
  197. it('cannot join idp-provisioned team', async function () {
  198. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  199. context: routerContext,
  200. });
  201. await userEvent.click(screen.getByText('Add Team'));
  202. await userEvent.hover(screen.queryByText('#idp-team'));
  203. expect(
  204. await screen.findByText(
  205. "Membership to this team is managed through your organization's identity provider."
  206. )
  207. ).toBeInTheDocument();
  208. });
  209. it('cannot change roles if member is idp-provisioned', function () {
  210. const roleRestrictedMember = TestStubs.Member({
  211. roles: TestStubs.OrgRoleList(),
  212. dateCreated: new Date(),
  213. teams: [team.slug],
  214. flags: {
  215. 'idp:role-restricted': true,
  216. },
  217. });
  218. MockApiClient.addMockResponse({
  219. url: `/organizations/${organization.slug}/members/${member.id}/`,
  220. body: roleRestrictedMember,
  221. });
  222. render(<OrganizationMemberDetail params={{memberId: roleRestrictedMember.id}} />, {
  223. context: routerContext,
  224. });
  225. const radios = screen.getAllByRole('radio');
  226. expect(radios.at(0)).toHaveAttribute('readonly');
  227. });
  228. });
  229. describe('Cannot Edit', function () {
  230. beforeEach(function () {
  231. organization = TestStubs.Organization({teams, access: ['org:read']});
  232. routerContext = TestStubs.routerContext([{organization}]);
  233. TeamStore.init();
  234. TeamStore.loadInitialData(teams);
  235. jest.resetAllMocks();
  236. MockApiClient.clearMockResponses();
  237. MockApiClient.addMockResponse({
  238. url: `/organizations/${organization.slug}/members/${member.id}/`,
  239. body: member,
  240. });
  241. MockApiClient.addMockResponse({
  242. url: `/organizations/${organization.slug}/members/${pendingMember.id}/`,
  243. body: pendingMember,
  244. });
  245. MockApiClient.addMockResponse({
  246. url: `/organizations/${organization.slug}/members/${expiredMember.id}/`,
  247. body: expiredMember,
  248. });
  249. MockApiClient.addMockResponse({
  250. url: `/organizations/${organization.slug}/teams/`,
  251. body: teams,
  252. });
  253. });
  254. it('can not change roles, teams, or save', function () {
  255. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  256. context: routerContext,
  257. });
  258. // Should have 4 roles
  259. const radios = screen.getAllByRole('radio');
  260. expect(radios.at(0)).toHaveAttribute('readonly');
  261. // Save Member
  262. expect(screen.getByRole('button', {name: 'Save Member'})).toBeDisabled();
  263. });
  264. });
  265. describe('Display status', function () {
  266. beforeEach(function () {
  267. organization = TestStubs.Organization({teams, access: ['org:read']});
  268. routerContext = TestStubs.routerContext([{organization}]);
  269. TeamStore.init();
  270. TeamStore.loadInitialData(teams);
  271. jest.resetAllMocks();
  272. MockApiClient.clearMockResponses();
  273. MockApiClient.addMockResponse({
  274. url: `/organizations/${organization.slug}/members/${member.id}/`,
  275. body: member,
  276. });
  277. MockApiClient.addMockResponse({
  278. url: `/organizations/${organization.slug}/members/${pendingMember.id}/`,
  279. body: pendingMember,
  280. });
  281. MockApiClient.addMockResponse({
  282. url: `/organizations/${organization.slug}/members/${expiredMember.id}/`,
  283. body: expiredMember,
  284. });
  285. MockApiClient.addMockResponse({
  286. url: `/organizations/${organization.slug}/teams/`,
  287. body: teams,
  288. });
  289. });
  290. it('display pending status', function () {
  291. render(<OrganizationMemberDetail params={{memberId: pendingMember.id}} />, {
  292. context: routerContext,
  293. });
  294. expect(screen.getByTestId('member-status')).toHaveTextContent('Invitation Pending');
  295. });
  296. it('display expired status', function () {
  297. render(<OrganizationMemberDetail params={{memberId: expiredMember.id}} />, {
  298. context: routerContext,
  299. });
  300. expect(screen.getByTestId('member-status')).toHaveTextContent('Invitation Expired');
  301. });
  302. });
  303. describe('Show resend button', function () {
  304. beforeEach(function () {
  305. organization = TestStubs.Organization({teams, access: ['org:read']});
  306. routerContext = TestStubs.routerContext([{organization}]);
  307. TeamStore.init();
  308. TeamStore.loadInitialData(teams);
  309. jest.resetAllMocks();
  310. MockApiClient.clearMockResponses();
  311. MockApiClient.addMockResponse({
  312. url: `/organizations/${organization.slug}/members/${member.id}/`,
  313. body: member,
  314. });
  315. MockApiClient.addMockResponse({
  316. url: `/organizations/${organization.slug}/members/${pendingMember.id}/`,
  317. body: pendingMember,
  318. });
  319. MockApiClient.addMockResponse({
  320. url: `/organizations/${organization.slug}/members/${expiredMember.id}/`,
  321. body: expiredMember,
  322. });
  323. MockApiClient.addMockResponse({
  324. url: `/organizations/${organization.slug}/teams/`,
  325. body: teams,
  326. });
  327. });
  328. it('shows for pending', function () {
  329. render(<OrganizationMemberDetail params={{memberId: pendingMember.id}} />, {
  330. context: routerContext,
  331. });
  332. expect(screen.getByRole('button', {name: 'Resend Invite'})).toBeInTheDocument();
  333. });
  334. it('does not show for expired', function () {
  335. render(<OrganizationMemberDetail params={{memberId: expiredMember.id}} />, {
  336. context: routerContext,
  337. });
  338. expect(
  339. screen.queryByRole('button', {name: 'Resend Invite'})
  340. ).not.toBeInTheDocument();
  341. });
  342. });
  343. describe('Reset member 2FA', function () {
  344. const fields = {
  345. roles: TestStubs.OrgRoleList(),
  346. dateCreated: new Date(),
  347. ...teamAssignment,
  348. };
  349. const noAccess = TestStubs.Member({
  350. ...fields,
  351. id: '4',
  352. user: TestStubs.User({has2fa: false}),
  353. });
  354. const no2fa = TestStubs.Member({
  355. ...fields,
  356. id: '5',
  357. user: TestStubs.User({has2fa: false, authenticators: [], canReset2fa: true}),
  358. });
  359. const has2fa = TestStubs.Member({
  360. ...fields,
  361. id: '6',
  362. user: TestStubs.User({
  363. has2fa: true,
  364. authenticators: [
  365. TestStubs.Authenticators().Totp(),
  366. TestStubs.Authenticators().Sms(),
  367. TestStubs.Authenticators().U2f(),
  368. ],
  369. canReset2fa: true,
  370. }),
  371. });
  372. const multipleOrgs = TestStubs.Member({
  373. ...fields,
  374. id: '7',
  375. user: TestStubs.User({
  376. has2fa: true,
  377. authenticators: [TestStubs.Authenticators().Totp()],
  378. canReset2fa: false,
  379. }),
  380. });
  381. beforeEach(function () {
  382. organization = TestStubs.Organization({teams});
  383. routerContext = TestStubs.routerContext([{organization}]);
  384. MockApiClient.clearMockResponses();
  385. MockApiClient.addMockResponse({
  386. url: `/organizations/${organization.slug}/members/${pendingMember.id}/`,
  387. body: pendingMember,
  388. });
  389. MockApiClient.addMockResponse({
  390. url: `/organizations/${organization.slug}/members/${noAccess.id}/`,
  391. body: noAccess,
  392. });
  393. MockApiClient.addMockResponse({
  394. url: `/organizations/${organization.slug}/members/${no2fa.id}/`,
  395. body: no2fa,
  396. });
  397. MockApiClient.addMockResponse({
  398. url: `/organizations/${organization.slug}/members/${has2fa.id}/`,
  399. body: has2fa,
  400. });
  401. MockApiClient.addMockResponse({
  402. url: `/organizations/${organization.slug}/members/${multipleOrgs.id}/`,
  403. body: multipleOrgs,
  404. });
  405. MockApiClient.addMockResponse({
  406. url: `/organizations/${organization.slug}/teams/`,
  407. body: teams,
  408. });
  409. });
  410. const button = () =>
  411. screen.queryByRole('button', {name: 'Reset two-factor authentication'});
  412. const tooltip = () => screen.queryByTestId('reset-2fa-tooltip');
  413. const expectButtonEnabled = () => {
  414. expect(button()).toHaveTextContent('Reset two-factor authentication');
  415. expect(button()).toBeEnabled();
  416. expect(tooltip()).not.toBeInTheDocument();
  417. };
  418. const expectButtonDisabled = async title => {
  419. expect(button()).toHaveTextContent('Reset two-factor authentication');
  420. expect(button()).toBeDisabled();
  421. await userEvent.hover(button());
  422. expect(await screen.findByText(title)).toBeInTheDocument();
  423. };
  424. it('does not show for pending member', function () {
  425. render(<OrganizationMemberDetail params={{memberId: pendingMember.id}} />, {
  426. context: routerContext,
  427. });
  428. expect(button()).not.toBeInTheDocument();
  429. });
  430. it('shows tooltip for joined member without permission to edit', async function () {
  431. render(<OrganizationMemberDetail params={{memberId: noAccess.id}} />, {
  432. context: routerContext,
  433. });
  434. await expectButtonDisabled('You do not have permission to perform this action');
  435. });
  436. it('shows tooltip for member without 2fa', async function () {
  437. render(<OrganizationMemberDetail params={{memberId: no2fa.id}} />, {
  438. context: routerContext,
  439. });
  440. await expectButtonDisabled('Not enrolled in two-factor authentication');
  441. });
  442. it('can reset member 2FA', async function () {
  443. const deleteMocks = has2fa.user.authenticators.map(auth =>
  444. MockApiClient.addMockResponse({
  445. url: `/users/${has2fa.user.id}/authenticators/${auth.id}/`,
  446. method: 'DELETE',
  447. })
  448. );
  449. render(<OrganizationMemberDetail params={{memberId: has2fa.id}} />, {
  450. context: routerContext,
  451. });
  452. renderGlobalModal();
  453. expectButtonEnabled();
  454. await userEvent.click(button());
  455. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  456. deleteMocks.forEach(deleteMock => {
  457. expect(deleteMock).toHaveBeenCalled();
  458. });
  459. });
  460. it('shows tooltip for member in multiple orgs', async function () {
  461. render(<OrganizationMemberDetail params={{memberId: multipleOrgs.id}} />, {
  462. context: routerContext,
  463. });
  464. await expectButtonDisabled(
  465. 'Cannot be reset since user is in more than one organization'
  466. );
  467. });
  468. it('shows tooltip for member in 2FA required org', async function () {
  469. organization.require2FA = true;
  470. MockApiClient.addMockResponse({
  471. url: `/organizations/${organization.slug}/members/${has2fa.id}/`,
  472. body: has2fa,
  473. });
  474. render(<OrganizationMemberDetail params={{memberId: has2fa.id}} />, {
  475. context: routerContext,
  476. });
  477. await expectButtonDisabled(
  478. 'Cannot be reset since two-factor is required for this organization'
  479. );
  480. });
  481. });
  482. describe('Org Roles affect Team Roles', () => {
  483. // Org Admin will be deprecated
  484. const admin = TestStubs.Member({
  485. id: '4',
  486. role: 'admin',
  487. roleName: 'Admin',
  488. orgRole: 'admin',
  489. ...teamAssignment,
  490. });
  491. const manager = TestStubs.Member({
  492. id: '5',
  493. role: 'manager',
  494. roleName: 'Manager',
  495. orgRole: 'manager',
  496. ...teamAssignment,
  497. });
  498. const owner = TestStubs.Member({
  499. id: '6',
  500. role: 'owner',
  501. roleName: 'Owner',
  502. orgRole: 'owner',
  503. ...teamAssignment,
  504. });
  505. beforeAll(() => {
  506. organization = TestStubs.Organization({teams, features: ['team-roles']});
  507. routerContext = TestStubs.routerContext([{organization}]);
  508. });
  509. beforeEach(() => {
  510. MockApiClient.clearMockResponses();
  511. MockApiClient.addMockResponse({
  512. url: `/organizations/${organization.slug}/members/${member.id}/`,
  513. body: member,
  514. });
  515. MockApiClient.addMockResponse({
  516. url: `/organizations/${organization.slug}/members/${admin.id}/`,
  517. body: admin,
  518. });
  519. MockApiClient.addMockResponse({
  520. url: `/organizations/${organization.slug}/members/${manager.id}/`,
  521. body: manager,
  522. });
  523. MockApiClient.addMockResponse({
  524. url: `/organizations/${organization.slug}/members/${owner.id}/`,
  525. body: owner,
  526. });
  527. });
  528. it('does not overwrite team-roles for org members', async () => {
  529. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  530. context: routerContext,
  531. });
  532. // Role info box is hidden
  533. expect(screen.queryByTestId('alert-role-overwrite')).not.toBeInTheDocument();
  534. // Dropdown has correct value set
  535. const teamRow = screen.getByTestId('team-row-for-member');
  536. const teamRoleSelect = within(teamRow).getByText('Contributor');
  537. // Dropdown options are not visible
  538. expect(screen.queryAllByText('...').length).toBe(0);
  539. // Dropdown can be opened
  540. selectEvent.openMenu(teamRoleSelect);
  541. expect(screen.queryAllByText('...').length).toBe(2);
  542. // Dropdown value can be changed
  543. await selectEvent.select(teamRoleSelect, ['Team Admin']);
  544. expect(teamRoleSelect).toHaveTextContent('Team Admin');
  545. });
  546. it('overwrite team-roles for org admin/manager/owner', () => {
  547. function testForOrgRole(testMember) {
  548. cleanup();
  549. render(<OrganizationMemberDetail params={{memberId: testMember.id}} />, {
  550. context: routerContext,
  551. });
  552. // Role info box is showed
  553. expect(screen.queryByTestId('alert-role-overwrite')).toBeInTheDocument();
  554. // Dropdown has correct value set
  555. const teamRow = screen.getByTestId('team-row-for-member');
  556. const teamRoleSelect = within(teamRow).getByText('Team Admin');
  557. // Dropdown options are not visible
  558. expect(screen.queryAllByText('...').length).toBe(0);
  559. // Dropdown cannot be opened
  560. selectEvent.openMenu(teamRoleSelect);
  561. expect(screen.queryAllByText('...').length).toBe(0);
  562. }
  563. for (const role of [admin, manager, owner]) {
  564. testForOrgRole(role);
  565. }
  566. });
  567. it('overwrites when changing from member to manager', async () => {
  568. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  569. context: routerContext,
  570. });
  571. // Role info box is hidden
  572. expect(screen.queryByTestId('alert-role-overwrite')).not.toBeInTheDocument();
  573. // Dropdown has correct value set
  574. const teamRow = screen.getByTestId('team-row-for-member');
  575. const teamRoleSelect = within(teamRow).getByText('Contributor');
  576. // Change member to owner
  577. const orgRoleRadio = screen.getAllByRole('radio');
  578. expect(orgRoleRadio).toHaveLength(4);
  579. await userEvent.click(orgRoleRadio.at(-1));
  580. expect(orgRoleRadio.at(-1)).toBeChecked();
  581. // Role info box is shown
  582. expect(screen.queryByTestId('alert-role-overwrite')).toBeInTheDocument();
  583. // Dropdown has correct value set
  584. within(teamRow).getByText('Team Admin');
  585. // Dropdown options are not visible
  586. expect(screen.queryAllByText('...').length).toBe(0);
  587. // Dropdown cannot be opened
  588. selectEvent.openMenu(teamRoleSelect);
  589. expect(screen.queryAllByText('...').length).toBe(0);
  590. });
  591. });
  592. it('overwrites when member joins a manager team', async () => {
  593. render(<OrganizationMemberDetail params={{memberId: member.id}} />, {
  594. context: routerContext,
  595. });
  596. // Role info box is hidden
  597. expect(screen.queryByTestId('alert-role-overwrite')).not.toBeInTheDocument();
  598. // Dropdown has correct value set
  599. const teamRow = screen.getByTestId('team-row-for-member');
  600. const teamRoleSelect = within(teamRow).getByText('Contributor');
  601. // Join manager team
  602. await userEvent.click(screen.getByText('Add Team'));
  603. // Click the first item
  604. await userEvent.click(screen.getByText('#manager-team'));
  605. // Role info box is shown
  606. expect(screen.queryByTestId('alert-role-overwrite')).toBeInTheDocument();
  607. // Dropdowns have correct value set
  608. const teamRows = screen.getAllByTestId('team-row-for-member');
  609. within(teamRows[0]).getByText('Team Admin');
  610. within(teamRows[1]).getByText('Team Admin');
  611. // Dropdown options are not visible
  612. expect(screen.queryAllByText('...').length).toBe(0);
  613. // Dropdown cannot be opened
  614. selectEvent.openMenu(teamRoleSelect);
  615. expect(screen.queryAllByText('...').length).toBe(0);
  616. });
  617. });