123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- import {mountWithTheme} from 'sentry-test/enzyme';
- import InviteMembersModal from 'sentry/components/modals/inviteMembersModal';
- import TeamStore from 'sentry/stores/teamStore';
- describe('InviteMembersModal', function () {
- const team = TestStubs.Team();
- const org = TestStubs.Organization({access: ['member:write'], teams: [team]});
- TeamStore.loadInitialData([team]);
- const modalProps = {
- Body: p => p.children,
- Header: p => p.children,
- Footer: p => p.children,
- };
- const noWriteOrg = TestStubs.Organization({
- access: [],
- });
- const roles = [
- {
- id: 'admin',
- name: 'Admin',
- desc: 'This is the admin role',
- allowed: true,
- },
- {
- id: 'member',
- name: 'Member',
- desc: 'This is the member role',
- allowed: true,
- },
- ];
- MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/members/me/`,
- method: 'GET',
- body: {roles},
- });
- it('renders', function () {
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- // Starts with one invite row
- expect(wrapper.find('StyledInviteRow')).toHaveLength(1);
- // We have two roles loaded from the members/me endpoint, defaulting to the
- // 'member' role.
- expect(wrapper.find('RoleSelectControl').props().roles).toHaveLength(roles.length);
- expect(wrapper.find('RoleSelectControl SingleValue').first().text()).toBe('Member');
- });
- it('renders without organization.access', function () {
- const organization = TestStubs.Organization({access: undefined});
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={organization} />
- );
- expect(wrapper.find('StyledInviteRow').exists()).toBe(true);
- });
- it('can add a second row', function () {
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- expect(wrapper.find('StyledInviteRow')).toHaveLength(1);
- wrapper.find('AddButton').simulate('click');
- expect(wrapper.find('StyledInviteRow')).toHaveLength(2);
- });
- it('errors on duplicate emails', function () {
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- wrapper.find('AddButton').simulate('click');
- expect(wrapper.find('StyledInviteRow')).toHaveLength(2);
- const rows = wrapper.find('StyledInviteRow');
- rows
- .at(0)
- .props()
- .onChangeEmails([{value: 'test@test.com'}]);
- rows
- .at(1)
- .props()
- .onChangeEmails([{value: 'test@test.com'}]);
- wrapper.update();
- expect(wrapper.find('StatusMessage[status="error"]').text()).toBe(
- 'Duplicate emails between invite rows.'
- );
- });
- it('indicates the total invites on the invite button', function () {
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- wrapper
- .find('StyledInviteRow')
- .first()
- .props()
- .onChangeEmails([{value: 'test1@test.com'}, {value: 'test2@test.com'}]);
- wrapper.update();
- expect(wrapper.find('Button[data-test-id="send-invites"]').text()).toBe(
- 'Send invites (2)'
- );
- });
- it('can be closed', function () {
- const close = jest.fn();
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} closeModal={close} />
- );
- wrapper.find('Button[data-test-id="cancel"]').simulate('click');
- expect(close).toHaveBeenCalled();
- });
- it('sends all successful invites', async function () {
- const createMemberMock = MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/members/`,
- method: 'POST',
- });
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- wrapper.find('AddButton').simulate('click');
- // Setup two rows, one email each, the first with a admin role.
- const inviteRowProps = wrapper.find('StyledInviteRow').first().props();
- inviteRowProps.onChangeEmails([{value: 'test1@test.com'}]);
- inviteRowProps.onChangeRole({value: 'admin'});
- inviteRowProps.onChangeTeams([{value: 'team1'}]);
- wrapper
- .find('StyledInviteRow')
- .at(1)
- .props()
- .onChangeEmails([{value: 'test2@test.com'}]);
- wrapper.update();
- wrapper.find('FooterContent Button[priority="primary"]').simulate('click');
- // Verify data sent to the backend
- expect(createMemberMock).toHaveBeenCalledTimes(2);
- expect(createMemberMock).toHaveBeenNthCalledWith(
- 1,
- `/organizations/${org.slug}/members/`,
- expect.objectContaining({
- data: {email: 'test1@test.com', role: 'admin', teams: ['team1']},
- })
- );
- expect(createMemberMock).toHaveBeenNthCalledWith(
- 2,
- `/organizations/${org.slug}/members/`,
- expect.objectContaining({
- data: {email: 'test2@test.com', role: 'member', teams: []},
- })
- );
- // Pending invites being created..
- expect(
- wrapper.find('InviteRowControl SelectControl EmailLabel LoadingIndicator')
- ).toHaveLength(2);
- expect(wrapper.find('Button[data-test-id="cancel"][disabled]').exists()).toBe(true);
- expect(wrapper.find('Button[data-test-id="send-invites"][disabled]').exists()).toBe(
- true
- );
- expect(wrapper.find('StatusMessage LoadingIndicator').exists()).toBe(true);
- // Await request completion
- await tick();
- wrapper.update();
- expect(wrapper.find('StatusMessage').text()).toBe('Sent 2 invites');
- expect(wrapper.find('Button[data-test-id="close"]').exists()).toBe(true);
- expect(wrapper.find('Button[data-test-id="send-more"]').exists()).toBe(true);
- expect(wrapper.find('SelectControl EmailLabel IconCheckmark').exists()).toBe(true);
- // Send more reset the modal
- wrapper.find('Button[data-test-id="send-more"]').simulate('click');
- expect(wrapper.find('InviteRowControl SelectControl EmailLabel').exists()).toBe(
- false
- );
- });
- it('marks failed invites', async function () {
- const faildCreateMemberMock = MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/members/`,
- method: 'POST',
- statusCode: 400,
- });
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} />
- );
- const inviteRowProps = wrapper.find('StyledInviteRow').first().props();
- inviteRowProps.onChangeEmails([{value: 'bademail'}]);
- wrapper.update();
- wrapper.find('FooterContent Button[priority="primary"]').simulate('click');
- expect(faildCreateMemberMock).toHaveBeenCalled();
- // Await request completion
- await tick();
- wrapper.update();
- expect(wrapper.find('StatusMessage').text()).toBe(
- 'Sent 0 invites, 1 failed to send.'
- );
- expect(wrapper.find('SelectControl EmailLabel IconWarning').exists()).toBe(true);
- });
- it('can send initial email', async function () {
- const createMemberMock = MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/members/`,
- method: 'POST',
- });
- const initialEmail = 'test@gmail.com';
- const initialData = [{emails: new Set([initialEmail])}];
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} initialData={initialData} />
- );
- expect(wrapper.find('MultiValue').first().text().includes(initialEmail)).toBe(true);
- wrapper.find('FooterContent Button[priority="primary"]').simulate('click');
- await tick();
- wrapper.update();
- expect(createMemberMock).toHaveBeenCalledWith(
- `/organizations/${org.slug}/members/`,
- expect.objectContaining({
- data: {email: initialEmail, role: 'member', teams: []},
- })
- );
- expect(wrapper.find('StatusMessage').text()).toBe('Sent 1 invite');
- });
- it('can send initial email with role and team', async function () {
- const createMemberMock = MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/members/`,
- method: 'POST',
- });
- const initialEmail = 'test@gmail.com';
- const role = 'admin';
- const initialData = [
- {emails: new Set([initialEmail]), role, teams: new Set([team.slug])},
- ];
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={org} initialData={initialData} />
- );
- expect(
- wrapper
- .find('SelectControl[data-test-id="select-emails"]')
- .text()
- .includes(initialEmail)
- ).toBe(true);
- expect(
- wrapper.find('SelectControl[data-test-id="select-role"]').text().toLowerCase()
- ).toBe(role);
- expect(
- wrapper
- .find('SelectControl[data-test-id="select-teams"]')
- .text()
- .includes(team.slug)
- ).toBe(true);
- wrapper.find('FooterContent Button[priority="primary"]').simulate('click');
- await tick();
- wrapper.update();
- expect(createMemberMock).toHaveBeenCalledWith(
- `/organizations/${org.slug}/members/`,
- expect.objectContaining({
- data: {email: initialEmail, role, teams: [team.slug]},
- })
- );
- expect(wrapper.find('StatusMessage').text()).toBe('Sent 1 invite');
- });
- describe('member invite request mode', function () {
- it('has adjusted wording', function () {
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={noWriteOrg} />
- );
- expect(wrapper.find('Button[data-test-id="send-invites"]').text()).toBe(
- 'Send invite request'
- );
- expect(wrapper.find('Heading Tooltip').exists()).toBe(true);
- });
- it('POSTS to the invite-request endpoint', function () {
- const createInviteRequestMock = MockApiClient.addMockResponse({
- url: `/organizations/${org.slug}/invite-requests/`,
- method: 'POST',
- });
- const wrapper = mountWithTheme(
- <InviteMembersModal {...modalProps} organization={noWriteOrg} />
- );
- const inviteRowProps = wrapper.find('StyledInviteRow').first().props();
- inviteRowProps.onChangeEmails([{value: 'test1@test.com'}]);
- inviteRowProps.onChangeRole({value: 'admin'});
- inviteRowProps.onChangeTeams([{value: 'team1'}]);
- wrapper
- .find('StyledInviteRow')
- .first()
- .props()
- .onChangeEmails([{value: 'test2@test.com'}]);
- wrapper.update();
- wrapper.find('FooterContent Button[priority="primary"]').simulate('click');
- expect(createInviteRequestMock).toHaveBeenCalledTimes(1);
- });
- });
- });
|