Commit 3b4faba7 authored by Doug Stull's avatar Doug Stull Committed by Phil Hughes

Add invite member link to comments

- guage interest
parent a50d1713
...@@ -11,10 +11,12 @@ import { ...@@ -11,10 +11,12 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { partition, isString } from 'lodash'; import { partition, isString } from 'lodash';
import Api from '~/api'; import Api from '~/api';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import GroupSelect from '~/invite_members/components/group_select.vue'; import GroupSelect from '~/invite_members/components/group_select.vue';
import MembersTokenSelect from '~/invite_members/components/members_token_select.vue'; import MembersTokenSelect from '~/invite_members/components/members_token_select.vue';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { INVITE_MEMBERS_IN_COMMENT } from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
export default { export default {
...@@ -122,8 +124,9 @@ export default { ...@@ -122,8 +124,9 @@ export default {
usersToAddById.map((user) => user.id).join(','), usersToAddById.map((user) => user.id).join(','),
]; ];
}, },
openModal({ inviteeType }) { openModal({ inviteeType, source }) {
this.inviteeType = inviteeType; this.inviteeType = inviteeType;
this.source = source;
this.$root.$emit(BV_SHOW_MODAL, this.modalId); this.$root.$emit(BV_SHOW_MODAL, this.modalId);
}, },
...@@ -138,6 +141,12 @@ export default { ...@@ -138,6 +141,12 @@ export default {
} }
this.closeModal(); this.closeModal();
}, },
trackInvite() {
if (this.source === INVITE_MEMBERS_IN_COMMENT) {
const tracking = new ExperimentTracking(INVITE_MEMBERS_IN_COMMENT);
tracking.event('comment_invite_success');
}
},
cancelInvite() { cancelInvite() {
this.selectedAccessLevel = this.defaultAccessLevel; this.selectedAccessLevel = this.defaultAccessLevel;
this.selectedDate = undefined; this.selectedDate = undefined;
...@@ -177,6 +186,8 @@ export default { ...@@ -177,6 +186,8 @@ export default {
promises.push(apiAddByUserId(this.id, this.addByUserIdPostData(usersToAddById))); promises.push(apiAddByUserId(this.id, this.addByUserIdPostData(usersToAddById)));
} }
this.trackInvite();
Promise.all(promises).then(this.showToastMessageSuccess).catch(this.showToastMessageError); Promise.all(promises).then(this.showToastMessageSuccess).catch(this.showToastMessageError);
}, },
inviteByEmailPostData(usersToInviteByEmail) { inviteByEmailPostData(usersToInviteByEmail) {
......
<script> <script>
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -26,10 +27,29 @@ export default { ...@@ -26,10 +27,29 @@ export default {
required: false, required: false,
default: undefined, default: undefined,
}, },
triggerSource: {
type: String,
required: false,
default: 'unknown',
},
trackExperiment: {
type: String,
required: false,
default: undefined,
},
},
mounted() {
this.trackExperimentOnShow();
}, },
methods: { methods: {
openModal() { openModal() {
eventHub.$emit('openModal', { inviteeType: 'members' }); eventHub.$emit('openModal', { inviteeType: 'members', source: this.triggerSource });
},
trackExperimentOnShow() {
if (this.trackExperiment) {
const tracking = new ExperimentTracking(this.trackExperiment);
tracking.event('comment_invite_shown');
}
}, },
}, },
}; };
......
export const SEARCH_DELAY = 200; export const SEARCH_DELAY = 200;
export const INVITE_MEMBERS_IN_COMMENT = 'invite_members_in_comment';
...@@ -3,6 +3,7 @@ import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable'; ...@@ -3,6 +3,7 @@ import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import initIssuableSidebar from '~/init_issuable_sidebar'; import initIssuableSidebar from '~/init_issuable_sidebar';
import initInviteMemberModal from '~/invite_member/init_invite_member_modal'; import initInviteMemberModal from '~/invite_member/init_invite_member_modal';
import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger'; import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import { IssuableType } from '~/issuable_show/constants'; import { IssuableType } from '~/issuable_show/constants';
import Issue from '~/issue'; import Issue from '~/issue';
import '~/notes/index'; import '~/notes/index';
...@@ -34,6 +35,7 @@ export default function initShowIssue() { ...@@ -34,6 +35,7 @@ export default function initShowIssue() {
initIssueHeaderActions(store); initIssueHeaderActions(store);
initSentryErrorStackTraceApp(); initSentryErrorStackTraceApp();
initRelatedMergeRequestsApp(); initRelatedMergeRequestsApp();
initInviteMembersModal();
import(/* webpackChunkName: 'design_management' */ '~/design_management') import(/* webpackChunkName: 'design_management' */ '~/design_management')
.then((module) => module.default()) .then((module) => module.default())
......
...@@ -5,6 +5,7 @@ import initPipelines from '~/commit/pipelines/pipelines_bundle'; ...@@ -5,6 +5,7 @@ import initPipelines from '~/commit/pipelines/pipelines_bundle';
import initIssuableSidebar from '~/init_issuable_sidebar'; import initIssuableSidebar from '~/init_issuable_sidebar';
import initInviteMemberModal from '~/invite_member/init_invite_member_modal'; import initInviteMemberModal from '~/invite_member/init_invite_member_modal';
import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger'; import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import { handleLocationHash } from '~/lib/utils/common_utils'; import { handleLocationHash } from '~/lib/utils/common_utils';
import StatusBox from '~/merge_request/components/status_box.vue'; import StatusBox from '~/merge_request/components/status_box.vue';
import initSourcegraph from '~/sourcegraph'; import initSourcegraph from '~/sourcegraph';
...@@ -20,6 +21,7 @@ export default function initMergeRequestShow() { ...@@ -20,6 +21,7 @@ export default function initMergeRequestShow() {
loadAwardsHandler(); loadAwardsHandler();
initInviteMemberModal(); initInviteMemberModal();
initInviteMemberTrigger(); initInviteMemberTrigger();
initInviteMembersModal();
const el = document.querySelector('.js-mr-status-box'); const el = document.querySelector('.js-mr-status-box');
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
......
<script> <script>
import { GlButton, GlLink, GlLoadingIcon, GlSprintf, GlIcon } from '@gitlab/ui'; import { GlButton, GlLink, GlLoadingIcon, GlSprintf, GlIcon } from '@gitlab/ui';
import { isExperimentVariant } from '~/experimentation/utils';
import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import { INVITE_MEMBERS_IN_COMMENT } from '~/invite_members/constants';
export default { export default {
inviteMembersInComment: INVITE_MEMBERS_IN_COMMENT,
components: { components: {
GlButton, GlButton,
GlLink, GlLink,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlIcon, GlIcon,
InviteMembersTrigger,
}, },
props: { props: {
markdownDocsPath: { markdownDocsPath: {
...@@ -29,6 +34,9 @@ export default { ...@@ -29,6 +34,9 @@ export default {
hasQuickActionsDocsPath() { hasQuickActionsDocsPath() {
return this.quickActionsDocsPath !== ''; return this.quickActionsDocsPath !== '';
}, },
inviteCommentEnabled() {
return isExperimentVariant(INVITE_MEMBERS_IN_COMMENT, 'invite_member_link');
},
}, },
}; };
</script> </script>
...@@ -37,9 +45,9 @@ export default { ...@@ -37,9 +45,9 @@ export default {
<div class="comment-toolbar clearfix"> <div class="comment-toolbar clearfix">
<div class="toolbar-text"> <div class="toolbar-text">
<template v-if="!hasQuickActionsDocsPath && markdownDocsPath"> <template v-if="!hasQuickActionsDocsPath && markdownDocsPath">
<gl-link :href="markdownDocsPath" target="_blank">{{ <gl-link :href="markdownDocsPath" target="_blank">
__('Markdown is supported') {{ __('Markdown is supported') }}
}}</gl-link> </gl-link>
</template> </template>
<template v-if="hasQuickActionsDocsPath && markdownDocsPath"> <template v-if="hasQuickActionsDocsPath && markdownDocsPath">
<gl-sprintf <gl-sprintf
...@@ -59,6 +67,16 @@ export default { ...@@ -59,6 +67,16 @@ export default {
</template> </template>
</div> </div>
<span v-if="canAttachFile" class="uploading-container"> <span v-if="canAttachFile" class="uploading-container">
<invite-members-trigger
v-if="inviteCommentEnabled"
classes="gl-mr-3 gl-vertical-align-text-bottom"
:display-text="s__('InviteMember|Invite Member')"
icon="assignee"
variant="link"
:track-experiment="$options.inviteMembersInComment"
:trigger-source="$options.inviteMembersInComment"
data-track-event="comment_invite_click"
/>
<span class="uploading-progress-container hide"> <span class="uploading-progress-container hide">
<gl-icon name="media" /> <gl-icon name="media" />
<span class="attaching-file-message"></span> <span class="attaching-file-message"></span>
......
...@@ -55,6 +55,15 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -55,6 +55,15 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
record_experiment_user(:invite_members_version_b) record_experiment_user(:invite_members_version_b)
experiment(:invite_members_in_comment, namespace: @project.root_ancestor) do |experiment_instance|
experiment_instance.exclude! unless helpers.can_import_members?
experiment_instance.use {}
experiment_instance.try(:invite_member_link) {}
experiment_instance.track(:view, property: @project.root_ancestor.id.to_s)
end
end end
around_action :allow_gitaly_ref_name_caching, only: [:discussions] around_action :allow_gitaly_ref_name_caching, only: [:discussions]
......
...@@ -45,6 +45,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -45,6 +45,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml)
record_experiment_user(:invite_members_version_b) record_experiment_user(:invite_members_version_b)
experiment(:invite_members_in_comment, namespace: @project.root_ancestor) do |experiment_instance|
experiment_instance.exclude! unless helpers.can_import_members?
experiment_instance.use {}
experiment_instance.try(:invite_member_link) {}
experiment_instance.track(:view, property: @project.root_ancestor.id.to_s)
end
end end
before_action do before_action do
......
...@@ -4,3 +4,4 @@ ...@@ -4,3 +4,4 @@
- page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues") - page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues")
= render 'projects/issuable/show', issuable: @issue = render 'projects/issuable/show', issuable: @issue
= render 'shared/issuable/invite_members_trigger', project: @project
...@@ -108,3 +108,6 @@ ...@@ -108,3 +108,6 @@
= render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit = render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit
#js-review-bar #js-review-bar
= render 'shared/issuable/invite_members_trigger', project: @project
- return unless can_import_members?
.js-invite-members-modal{ data: { id: project.id,
name: project.name,
is_project: 'true',
access_levels: ProjectMember.access_level_roles.to_json,
default_access_level: Gitlab::Access::GUEST,
help_link: help_page_url('user/permissions') } }
---
name: invite_members_in_comment
introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51400'
rollout_issue_url: 'https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/300'
milestone: '13.10'
type: experiment
group: group::expansion
default_enabled: false
...@@ -16875,6 +16875,9 @@ msgstr "" ...@@ -16875,6 +16875,9 @@ msgstr ""
msgid "InviteMember|Don't worry, you can always invite teammates later" msgid "InviteMember|Don't worry, you can always invite teammates later"
msgstr "" msgstr ""
msgid "InviteMember|Invite Member"
msgstr ""
msgid "InviteMember|Invite Members (optional)" msgid "InviteMember|Invite Members (optional)"
msgstr "" msgstr ""
......
...@@ -209,6 +209,32 @@ RSpec.describe Projects::IssuesController do ...@@ -209,6 +209,32 @@ RSpec.describe Projects::IssuesController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issue_email_participants']).to contain_exactly({ "email" => participants[0].email }, { "email" => participants[1].email }) expect(json_response['issue_email_participants']).to contain_exactly({ "email" => participants[0].email }, { "email" => participants[1].email })
end end
context 'with the invite_members_in_comment experiment', :experiment do
context 'when user can invite' do
before do
stub_experiments(invite_members_in_comment: :invite_member_link)
project.add_maintainer(user)
end
it 'assigns the candidate experience and tracks the event' do
expect(experiment(:invite_member_link)).to track(:view, property: project.root_ancestor.id.to_s)
.on_any_instance
.for(:invite_member_link)
.with_context(namespace: project.root_ancestor)
get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }
end
end
context 'when user can not invite' do
it 'does not track the event' do
expect(experiment(:invite_member_link)).not_to track(:view)
get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }
end
end
end
end end
describe 'GET #new' do describe 'GET #new' do
......
...@@ -40,6 +40,32 @@ RSpec.describe Projects::MergeRequestsController do ...@@ -40,6 +40,32 @@ RSpec.describe Projects::MergeRequestsController do
get :show, params: params.merge(extra_params) get :show, params: params.merge(extra_params)
end end
context 'with the invite_members_in_comment experiment', :experiment do
context 'when user can invite' do
before do
stub_experiments(invite_members_in_comment: :invite_member_link)
project.add_maintainer(user)
end
it 'assigns the candidate experience and tracks the event' do
expect(experiment(:invite_member_link)).to track(:view, property: project.root_ancestor.id.to_s)
.on_any_instance
.for(:invite_member_link)
.with_context(namespace: project.root_ancestor)
go
end
end
context 'when user can not invite' do
it 'does not track the event' do
expect(experiment(:invite_member_link)).not_to track(:view)
go
end
end
end
context 'with view param' do context 'with view param' do
before do before do
go(view: 'parallel') go(view: 'parallel')
......
# frozen_string_literal: true
require "spec_helper"
RSpec.describe "User invites from a comment", :js do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { project.owner }
before do
sign_in(user)
end
it "launches the invite modal from invite link on a comment" do
stub_experiments(invite_members_in_comment: :invite_member_link)
visit project_issue_path(project, issue)
page.within(".new-note") do
click_button 'Invite Member'
end
expect(page).to have_content("You're inviting members to the")
end
end
# frozen_string_literal: true
require "spec_helper"
RSpec.describe "User invites from a comment", :js do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
let_it_be(:user) { project.owner }
before do
sign_in(user)
end
it "launches the invite modal from invite link on a comment" do
stub_experiments(invite_members_in_comment: :invite_member_link)
visit project_merge_request_path(project, merge_request)
page.within(".new-note") do
click_button 'Invite Member'
end
expect(page).to have_content("You're inviting members to the")
end
end
...@@ -3,7 +3,11 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -3,7 +3,11 @@ import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api'; import Api from '~/api';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue'; import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue';
import { INVITE_MEMBERS_IN_COMMENT } from '~/invite_members/constants';
jest.mock('~/experimentation/experiment_tracking');
const id = '1'; const id = '1';
const name = 'test name'; const name = 'test name';
...@@ -303,6 +307,7 @@ describe('InviteMembersModal', () => { ...@@ -303,6 +307,7 @@ describe('InviteMembersModal', () => {
jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData }); jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData }); jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
jest.spyOn(wrapper.vm, 'showToastMessageSuccess'); jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
jest.spyOn(wrapper.vm, 'trackInvite');
clickInviteButton(); clickInviteButton();
}); });
...@@ -396,5 +401,46 @@ describe('InviteMembersModal', () => { ...@@ -396,5 +401,46 @@ describe('InviteMembersModal', () => {
}); });
}); });
}); });
describe('tracking', () => {
const postData = {
user_id: '1',
access_level: defaultAccessLevel,
expires_at: undefined,
format: 'json',
};
beforeEach(() => {
wrapper = createComponent({ newUsersToInvite: [user3] });
wrapper.vm.$toast = { show: jest.fn() };
jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
});
it('tracks the invite', () => {
wrapper.vm.openModal({ inviteeType: 'members', source: INVITE_MEMBERS_IN_COMMENT });
clickInviteButton();
expect(ExperimentTracking).toHaveBeenCalledWith(INVITE_MEMBERS_IN_COMMENT);
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith('comment_invite_success');
});
it('does not track invite for unknown source', () => {
wrapper.vm.openModal({ inviteeType: 'members', source: 'unknown' });
clickInviteButton();
expect(ExperimentTracking).not.toHaveBeenCalled();
});
it('does not track invite undefined source', () => {
wrapper.vm.openModal({ inviteeType: 'members' });
clickInviteButton();
expect(ExperimentTracking).not.toHaveBeenCalled();
});
});
}); });
}); });
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import eventHub from '~/invite_members/event_hub';
jest.mock('~/experimentation/experiment_tracking');
const displayText = 'Invite team members'; const displayText = 'Invite team members';
let wrapper;
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
return shallowMount(InviteMembersTrigger, { wrapper = shallowMount(InviteMembersTrigger, {
propsData: { propsData: {
displayText, displayText,
...props, ...props,
...@@ -14,7 +19,7 @@ const createComponent = (props = {}) => { ...@@ -14,7 +19,7 @@ const createComponent = (props = {}) => {
}; };
describe('InviteMembersTrigger', () => { describe('InviteMembersTrigger', () => {
let wrapper; const findButton = () => wrapper.findComponent(GlButton);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -22,14 +27,52 @@ describe('InviteMembersTrigger', () => { ...@@ -22,14 +27,52 @@ describe('InviteMembersTrigger', () => {
}); });
describe('displayText', () => { describe('displayText', () => {
const findButton = () => wrapper.findComponent(GlButton); it('includes the correct displayText for the button', () => {
createComponent();
expect(findButton().text()).toBe(displayText);
});
});
describe('clicking the link', () => {
let spy;
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); spy = jest.spyOn(eventHub, '$emit');
}); });
it('includes the correct displayText for the button', () => { it('emits openModal from an unknown source', () => {
expect(findButton().text()).toBe(displayText); createComponent();
findButton().vm.$emit('click');
expect(spy).toHaveBeenCalledWith('openModal', { inviteeType: 'members', source: 'unknown' });
});
it('emits openModal from a named source', () => {
createComponent({ triggerSource: '_trigger_source_' });
findButton().vm.$emit('click');
expect(spy).toHaveBeenCalledWith('openModal', {
inviteeType: 'members',
source: '_trigger_source_',
});
});
});
describe('tracking', () => {
it('tracks on mounting', () => {
createComponent({ trackExperiment: '_track_experiment_' });
expect(ExperimentTracking).toHaveBeenCalledWith('_track_experiment_');
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith('comment_invite_shown');
});
it('does not track on mounting', () => {
createComponent();
expect(ExperimentTracking).not.toHaveBeenCalledWith('_track_experiment_');
}); });
}); });
}); });
import Vue from 'vue'; import { mount } from '@vue/test-utils';
import mountComponent from 'helpers/vue_mount_component_helper'; import { isExperimentVariant } from '~/experimentation/utils';
import toolbar from '~/vue_shared/components/markdown/toolbar.vue'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import { INVITE_MEMBERS_IN_COMMENT } from '~/invite_members/constants';
import Toolbar from '~/vue_shared/components/markdown/toolbar.vue';
jest.mock('~/experimentation/utils', () => ({ isExperimentVariant: jest.fn() }));
describe('toolbar', () => { describe('toolbar', () => {
let vm; let wrapper;
const Toolbar = Vue.extend(toolbar);
const props = { const createMountedWrapper = (props = {}) => {
markdownDocsPath: '', wrapper = mount(Toolbar, {
propsData: { markdownDocsPath: '', ...props },
stubs: { 'invite-members-trigger': true },
});
}; };
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
isExperimentVariant.mockReset();
}); });
describe('user can attach file', () => { describe('user can attach file', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Toolbar, props); createMountedWrapper();
}); });
it('should render uploading-container', () => { it('should render uploading-container', () => {
expect(vm.$el.querySelector('.uploading-container')).not.toBeNull(); expect(wrapper.vm.$el.querySelector('.uploading-container')).not.toBeNull();
}); });
}); });
describe('user cannot attach file', () => { describe('user cannot attach file', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Toolbar, { ...props, canAttachFile: false }); createMountedWrapper({ canAttachFile: false });
}); });
it('should not render uploading-container', () => { it('should not render uploading-container', () => {
expect(vm.$el.querySelector('.uploading-container')).toBeNull(); expect(wrapper.vm.$el.querySelector('.uploading-container')).toBeNull();
});
});
describe('user can invite member', () => {
const findInviteLink = () => wrapper.find(InviteMembersTrigger);
beforeEach(() => {
isExperimentVariant.mockReturnValue(true);
createMountedWrapper();
});
it('should render the invite members trigger', () => {
expect(findInviteLink().exists()).toBe(true);
});
it('should have correct props', () => {
expect(findInviteLink().props().displayText).toBe('Invite Member');
expect(findInviteLink().props().trackExperiment).toBe(INVITE_MEMBERS_IN_COMMENT);
expect(findInviteLink().props().triggerSource).toBe(INVITE_MEMBERS_IN_COMMENT);
});
});
describe('user can not invite member', () => {
const findInviteLink = () => wrapper.find(InviteMembersTrigger);
beforeEach(() => {
isExperimentVariant.mockReturnValue(false);
createMountedWrapper();
});
it('should render the invite members trigger', () => {
expect(findInviteLink().exists()).toBe(false);
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment