Commit db80e730 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch...

Merge branch '333449-experiment-ask-admin-inviter-what-areas-they-d-like-the-invitee-to-focus-on-5' into 'master'

Experiment: add area of focus question to invite modal

See merge request gitlab-org/gitlab!65273
parents a6708159 fa18ec94
......@@ -9,13 +9,14 @@ import {
GlSprintf,
GlButton,
GlFormInput,
GlFormCheckboxGroup,
} from '@gitlab/ui';
import { partition, isString } from 'lodash';
import Api from '~/api';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__, sprintf } from '~/locale';
import { INVITE_MEMBERS_IN_COMMENT, GROUP_FILTERS } from '../constants';
import { INVITE_MEMBERS_IN_COMMENT, GROUP_FILTERS, MEMBER_AREAS_OF_FOCUS } from '../constants';
import eventHub from '../event_hub';
import {
responseMessageFromError,
......@@ -36,6 +37,7 @@ export default {
GlSprintf,
GlButton,
GlFormInput,
GlFormCheckboxGroup,
MembersTokenSelect,
GroupSelect,
},
......@@ -74,6 +76,14 @@ export default {
type: String,
required: true,
},
areasOfFocusOptions: {
type: Array,
required: true,
},
noSelectionAreasOfFocus: {
type: Array,
required: true,
},
},
data() {
return {
......@@ -83,6 +93,7 @@ export default {
inviteeType: 'members',
newUsersToInvite: [],
selectedDate: undefined,
selectedAreasOfFocus: [],
groupToBeSharedWith: {},
source: 'unknown',
invalidFeedbackMessage: '',
......@@ -128,10 +139,21 @@ export default {
this.newUsersToInvite.length === 0 && Object.keys(this.groupToBeSharedWith).length === 0
);
},
areasOfFocusEnabled() {
return this.areasOfFocusOptions.length !== 0;
},
areasOfFocusForPost() {
if (this.selectedAreasOfFocus.length === 0 && this.areasOfFocusEnabled) {
return this.noSelectionAreasOfFocus;
}
return this.selectedAreasOfFocus;
},
},
mounted() {
eventHub.$on('openModal', (options) => {
this.openModal(options);
this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.view);
});
},
methods: {
......@@ -152,9 +174,12 @@ export default {
this.$root.$emit(BV_SHOW_MODAL, this.modalId);
},
trackEvent(experimentName, eventName) {
const tracking = new ExperimentTracking(experimentName);
tracking.event(eventName);
},
closeModal() {
this.resetFields();
this.$root.$emit(BV_HIDE_MODAL, this.modalId);
this.$refs.modal.hide();
},
sendInvite() {
if (this.isInviteGroup) {
......@@ -165,9 +190,10 @@ export default {
},
trackInvite() {
if (this.source === INVITE_MEMBERS_IN_COMMENT) {
const tracking = new ExperimentTracking(INVITE_MEMBERS_IN_COMMENT);
tracking.event('comment_invite_success');
this.trackEvent(INVITE_MEMBERS_IN_COMMENT, 'comment_invite_success');
}
this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.submit);
},
resetFields() {
this.isLoading = false;
......@@ -176,6 +202,7 @@ export default {
this.newUsersToInvite = [];
this.groupToBeSharedWith = {};
this.invalidFeedbackMessage = '';
this.selectedAreasOfFocus = [];
},
changeSelectedItem(item) {
this.selectedAccessLevel = item;
......@@ -223,6 +250,7 @@ export default {
email: usersToInviteByEmail,
access_level: this.selectedAccessLevel,
invite_source: this.source,
areas_of_focus: this.areasOfFocusForPost,
};
},
addByUserIdPostData(usersToAddById) {
......@@ -231,6 +259,7 @@ export default {
user_id: usersToAddById,
access_level: this.selectedAccessLevel,
invite_source: this.source,
areas_of_focus: this.areasOfFocusForPost,
};
},
shareWithGroupPostData(groupToBeSharedWith) {
......@@ -304,18 +333,22 @@ export default {
inviteButtonText: s__('InviteMembersModal|Invite'),
cancelButtonText: s__('InviteMembersModal|Cancel'),
headerCloseLabel: s__('InviteMembersModal|Close invite team members'),
areasOfFocusLabel: s__(
'InviteMembersModal|What would you like new member(s) to focus on? (optional)',
),
},
membersTokenSelectLabelId: 'invite-members-input',
};
</script>
<template>
<gl-modal
ref="modal"
:modal-id="modalId"
size="sm"
data-qa-selector="invite_members_modal_content"
:title="$options.labels[inviteeType].modalTitle"
:header-close-label="$options.labels.headerCloseLabel"
@close="resetFields"
@hidden="resetFields"
>
<div>
<p ref="introText">
......@@ -351,7 +384,7 @@ export default {
/>
</gl-form-group>
<label class="gl-font-weight-bold gl-mt-3">{{ $options.labels.accessLevel }}</label>
<label class="gl-mt-3">{{ $options.labels.accessLevel }}</label>
<div class="gl-mt-2 gl-w-half gl-xs-w-full">
<gl-dropdown
class="gl-shadow-none gl-w-full"
......@@ -381,7 +414,7 @@ export default {
</gl-sprintf>
</div>
<label class="gl-font-weight-bold gl-mt-5 gl-display-block" for="expires_at">{{
<label class="gl-mt-5 gl-display-block" for="expires_at">{{
$options.labels.accessExpireDate
}}</label>
<div class="gl-mt-2 gl-w-half gl-xs-w-full gl-display-inline-block">
......@@ -400,6 +433,16 @@ export default {
</template>
</gl-datepicker>
</div>
<div v-if="areasOfFocusEnabled">
<label class="gl-mt-5">
{{ $options.labels.areasOfFocusLabel }}
</label>
<gl-form-checkbox-group
v-model="selectedAreasOfFocus"
:options="areasOfFocusOptions"
data-testid="area-of-focus-checks"
/>
</div>
</div>
<template #modal-footer>
......
......@@ -3,6 +3,11 @@ import { __ } from '~/locale';
export const SEARCH_DELAY = 200;
export const INVITE_MEMBERS_IN_COMMENT = 'invite_members_in_comment';
export const MEMBER_AREAS_OF_FOCUS = {
name: 'member_areas_of_focus',
view: 'view',
submit: 'submit',
};
export const GROUP_FILTERS = {
ALL: 'all',
......
......@@ -23,6 +23,8 @@ export default function initInviteMembersModal() {
defaultAccessLevel: parseInt(el.dataset.defaultAccessLevel, 10),
groupSelectFilter: el.dataset.groupsFilter,
groupSelectParentId: parseInt(el.dataset.parentId, 10),
areasOfFocusOptions: JSON.parse(el.dataset.areasOfFocusOptions),
noSelectionAreasOfFocus: JSON.parse(el.dataset.noSelectionAreasOfFocus),
},
}),
});
......
......@@ -39,4 +39,43 @@ module InviteMembersHelper
{}
end
end
def common_invite_modal_dataset(source)
dataset = {
id: source.id,
name: source.name,
default_access_level: Gitlab::Access::GUEST
}
experiment(:member_areas_of_focus, user: current_user) do |e|
e.publish_to_database
e.control { dataset.merge!(areas_of_focus_options: [], no_selection_areas_of_focus: []) }
e.candidate { dataset.merge!(areas_of_focus_options: member_areas_of_focus_options.to_json, no_selection_areas_of_focus: ['no_selection']) }
end
dataset
end
private
def member_areas_of_focus_options
[
{
value: 'Contribute to the codebase', text: s_('InviteMembersModal|Contribute to the codebase')
},
{
value: 'Collaborate on open issues and merge requests', text: s_('InviteMembersModal|Collaborate on open issues and merge requests')
},
{
value: 'Configure CI/CD', text: s_('InviteMembersModal|Configure CI/CD')
},
{
value: 'Configure security features', text: s_('InviteMembersModal|Configure security features')
},
{
value: 'Other', text: s_('InviteMembersModal|Other')
}
]
end
end
- return unless can_manage_members?(group)
.js-invite-members-modal{ data: { id: group.id,
name: group.name,
is_project: 'false',
.js-invite-members-modal{ data: { is_project: 'false',
access_levels: GroupMember.access_level_roles.to_json,
default_access_level: Gitlab::Access::GUEST,
help_link: help_page_url('user/permissions') }.merge(group_select_data(group)) }
help_link: help_page_url('user/permissions') }.merge(group_select_data(group)).merge(common_invite_modal_dataset(group)) }
- return unless can_import_members?
.js-invite-members-modal{ data: { id: project.id,
name: project.name,
is_project: 'true',
.js-invite-members-modal{ data: { is_project: 'true',
access_levels: ProjectMember.access_level_roles.to_json,
default_access_level: Gitlab::Access::GUEST,
help_link: help_page_url('user/permissions') } }
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(project)) }
---
name: member_areas_of_focus
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65273
rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/406
milestone: '14.2'
type: experiment
group: group::expansion
default_enabled: false
......@@ -18032,6 +18032,18 @@ msgstr ""
msgid "InviteMembersModal|Close invite team members"
msgstr ""
msgid "InviteMembersModal|Collaborate on open issues and merge requests"
msgstr ""
msgid "InviteMembersModal|Configure CI/CD"
msgstr ""
msgid "InviteMembersModal|Configure security features"
msgstr ""
msgid "InviteMembersModal|Contribute to the codebase"
msgstr ""
msgid "InviteMembersModal|GitLab member or email address"
msgstr ""
......@@ -18047,6 +18059,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
msgid "InviteMembersModal|Other"
msgstr ""
msgid "InviteMembersModal|Search for a group to invite"
msgstr ""
......@@ -18062,6 +18077,9 @@ msgstr ""
msgid "InviteMembersModal|Something went wrong"
msgstr ""
msgid "InviteMembersModal|What would you like new member(s) to focus on? (optional)"
msgstr ""
msgid "InviteMembersModal|You're inviting a group to the %{strongStart}%{name}%{strongEnd} group."
msgstr ""
......
......@@ -84,6 +84,33 @@ RSpec.describe 'Groups > Members > Manage members' do
property: 'existing_user',
user: user1
)
expect_no_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus'
)
end
it 'adds a user to group with area_of_focus', :js, :snowplow, :aggregate_failures do
stub_experiments(member_areas_of_focus: :candidate)
group.add_owner(user1)
visit group_group_members_path(group)
invite_member(user2.name, role: 'Reporter', area_of_focus: true)
wait_for_requests
expect_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus',
label: 'Contribute to the codebase',
property: group.members.last.id.to_s
)
expect_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus',
label: 'Collaborate on open issues and merge requests',
property: group.members.last.id.to_s
)
end
it 'do not disclose email addresses', :js do
......@@ -193,9 +220,36 @@ RSpec.describe 'Groups > Members > Manage members' do
property: 'net_new_user',
user: user1
)
expect_no_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus'
)
end
end
it 'invite user to group with area_of_focus', :js, :snowplow, :aggregate_failures do
stub_experiments(member_areas_of_focus: :candidate)
group.add_owner(user1)
visit group_group_members_path(group)
invite_member('test@example.com', role: 'Reporter', area_of_focus: true)
wait_for_requests
expect_snowplow_event(
category: 'Members::InviteService',
action: 'area_of_focus',
label: 'Contribute to the codebase',
property: group.members.last.id.to_s
)
expect_snowplow_event(
category: 'Members::InviteService',
action: 'area_of_focus',
label: 'Collaborate on open issues and merge requests',
property: group.members.last.id.to_s
)
end
context 'when user is a guest' do
before do
group.add_guest(user1)
......
......@@ -6,6 +6,7 @@ import {
GlSprintf,
GlLink,
GlModal,
GlFormCheckboxGroup,
} from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { stubComponent } from 'helpers/stub_component';
......@@ -15,7 +16,8 @@ import Api from '~/api';
import ExperimentTracking from '~/experimentation/experiment_tracking';
import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue';
import MembersTokenSelect from '~/invite_members/components/members_token_select.vue';
import { INVITE_MEMBERS_IN_COMMENT } from '~/invite_members/constants';
import { INVITE_MEMBERS_IN_COMMENT, MEMBER_AREAS_OF_FOCUS } from '~/invite_members/constants';
import eventHub from '~/invite_members/event_hub';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { apiPaths, membersApiResponse, invitationsApiResponse } from '../mock_data/api_responses';
......@@ -32,7 +34,12 @@ const inviteeType = 'members';
const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 };
const defaultAccessLevel = 10;
const inviteSource = 'unknown';
const noSelectionAreasOfFocus = ['no_selection'];
const helpLink = 'https://example.com';
const areasOfFocusOptions = [
{ text: 'area1', value: 'area1' },
{ text: 'area2', value: 'area2' },
];
const user1 = { id: 1, name: 'Name One', username: 'one_1', avatar_url: '' };
const user2 = { id: 2, name: 'Name Two', username: 'one_2', avatar_url: '' };
......@@ -58,7 +65,9 @@ const createComponent = (data = {}, props = {}) => {
isProject,
inviteeType,
accessLevels,
areasOfFocusOptions,
defaultAccessLevel,
noSelectionAreasOfFocus,
helpLink,
...props,
},
......@@ -119,6 +128,7 @@ describe('InviteMembersModal', () => {
const findMembersFormGroup = () => wrapper.findByTestId('members-form-group');
const membersFormGroupInvalidFeedback = () => findMembersFormGroup().props('invalidFeedback');
const findMembersSelect = () => wrapper.findComponent(MembersTokenSelect);
const findAreaofFocusCheckBoxGroup = () => wrapper.findComponent(GlFormCheckboxGroup);
describe('rendering the modal', () => {
beforeEach(() => {
......@@ -164,6 +174,21 @@ describe('InviteMembersModal', () => {
});
});
describe('rendering the areas_of_focus', () => {
it('renders the areas_of_focus checkboxes', () => {
createComponent();
expect(findAreaofFocusCheckBoxGroup().props('options')).toBe(areasOfFocusOptions);
expect(findAreaofFocusCheckBoxGroup().exists()).toBe(true);
});
it('does not render the areas_of_focus checkboxes', () => {
createComponent({}, { areasOfFocusOptions: [] });
expect(findAreaofFocusCheckBoxGroup().exists()).toBe(false);
});
});
describe('displaying the correct introText', () => {
describe('when inviting to a project', () => {
describe('when inviting members', () => {
......@@ -214,6 +239,20 @@ describe('InviteMembersModal', () => {
"email 'email@example.com' does not match the allowed domains: example1.org";
const expectedSyntaxError = 'email contains an invalid email address';
it('calls the API with the expected focus data when an areas_of_focus checkbox is clicked', () => {
const spy = jest.spyOn(Api, 'addGroupMembersByUserId');
const expectedFocus = [areasOfFocusOptions[0].value];
createComponent({ newUsersToInvite: [user1] });
findAreaofFocusCheckBoxGroup().vm.$emit('input', expectedFocus);
clickInviteButton();
expect(spy).toHaveBeenCalledWith(
user1.id.toString(),
expect.objectContaining({ areas_of_focus: expectedFocus }),
);
});
describe('when inviting an existing user to group by user ID', () => {
const postData = {
user_id: '1,2',
......@@ -221,6 +260,7 @@ describe('InviteMembersModal', () => {
expires_at: undefined,
invite_source: inviteSource,
format: 'json',
areas_of_focus: noSelectionAreasOfFocus,
};
describe('when member is added successfully', () => {
......@@ -230,30 +270,34 @@ describe('InviteMembersModal', () => {
wrapper.vm.$toast = { show: jest.fn() };
jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
clickInviteButton();
});
it('sets isLoading on the Invite button when it is clicked', () => {
expect(findInviteButton().props('loading')).toBe(true);
});
it('includes the non-default selected areas of focus', () => {
const focus = ['abc'];
const updatedPostData = { ...postData, areas_of_focus: focus };
wrapper.setData({ selectedAreasOfFocus: focus });
it('removes isLoading from the Invite button when request completes', async () => {
await waitForPromises();
clickInviteButton();
expect(findInviteButton().props('loading')).toBe(false);
expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, updatedPostData);
});
it('calls Api addGroupMembersByUserId with the correct params', async () => {
await waitForPromises;
describe('when triggered from regular mounting', () => {
beforeEach(() => {
clickInviteButton();
});
expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, postData);
});
it('sets isLoading on the Invite button when it is clicked', () => {
expect(findInviteButton().props('loading')).toBe(true);
});
it('displays the successful toastMessage', async () => {
await waitForPromises;
it('calls Api addGroupMembersByUserId with the correct params', () => {
expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, postData);
});
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
it('displays the successful toastMessage', () => {
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
});
});
});
......@@ -353,6 +397,7 @@ describe('InviteMembersModal', () => {
expires_at: undefined,
email: 'email@example.com',
invite_source: inviteSource,
areas_of_focus: noSelectionAreasOfFocus,
format: 'json',
};
......@@ -363,16 +408,30 @@ describe('InviteMembersModal', () => {
wrapper.vm.$toast = { show: jest.fn() };
jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
});
it('includes the non-default selected areas of focus', () => {
const focus = ['abc'];
const updatedPostData = { ...postData, areas_of_focus: focus };
wrapper.setData({ selectedAreasOfFocus: focus });
clickInviteButton();
});
it('calls Api inviteGroupMembersByEmail with the correct params', () => {
expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, postData);
expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, updatedPostData);
});
it('displays the successful toastMessage', () => {
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
describe('when triggered from regular mounting', () => {
beforeEach(() => {
clickInviteButton();
});
it('calls Api inviteGroupMembersByEmail with the correct params', () => {
expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, postData);
});
it('displays the successful toastMessage', () => {
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
});
});
});
......@@ -465,6 +524,7 @@ describe('InviteMembersModal', () => {
access_level: defaultAccessLevel,
expires_at: undefined,
invite_source: inviteSource,
areas_of_focus: noSelectionAreasOfFocus,
format: 'json',
};
......@@ -501,7 +561,7 @@ describe('InviteMembersModal', () => {
});
it('calls Apis with the invite source passed through to openModal', () => {
wrapper.vm.openModal({ inviteeType: 'members', source: '_invite_source_' });
eventHub.$emit('openModal', { inviteeType: 'members', source: '_invite_source_' });
clickInviteButton();
......@@ -579,9 +639,7 @@ describe('InviteMembersModal', () => {
clickInviteButton();
});
it('displays the generic error message', async () => {
await waitForPromises();
it('displays the generic error message', () => {
expect(membersFormGroupInvalidFeedback()).toBe('Something went wrong');
});
});
......@@ -596,7 +654,7 @@ describe('InviteMembersModal', () => {
});
it('tracks the invite', () => {
wrapper.vm.openModal({ inviteeType: 'members', source: INVITE_MEMBERS_IN_COMMENT });
eventHub.$emit('openModal', { inviteeType: 'members', source: INVITE_MEMBERS_IN_COMMENT });
clickInviteButton();
......@@ -605,19 +663,37 @@ describe('InviteMembersModal', () => {
});
it('does not track invite for unknown source', () => {
wrapper.vm.openModal({ inviteeType: 'members', source: 'unknown' });
eventHub.$emit('openModal', { inviteeType: 'members', source: 'unknown' });
clickInviteButton();
expect(ExperimentTracking).not.toHaveBeenCalled();
expect(ExperimentTracking).not.toHaveBeenCalledWith(INVITE_MEMBERS_IN_COMMENT);
});
it('does not track invite undefined source', () => {
wrapper.vm.openModal({ inviteeType: 'members' });
eventHub.$emit('openModal', { inviteeType: 'members' });
clickInviteButton();
expect(ExperimentTracking).not.toHaveBeenCalledWith(INVITE_MEMBERS_IN_COMMENT);
});
it('tracks the view for areas_of_focus', () => {
eventHub.$emit('openModal', { inviteeType: 'members' });
expect(ExperimentTracking).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.name);
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.view);
});
it('tracks the invite for areas_of_focus', () => {
eventHub.$emit('openModal', { inviteeType: 'members' });
clickInviteButton();
expect(ExperimentTracking).not.toHaveBeenCalled();
expect(ExperimentTracking).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.name);
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(
MEMBER_AREAS_OF_FOCUS.submit,
);
});
});
});
......
......@@ -14,6 +14,56 @@ RSpec.describe InviteMembersHelper do
helper.extend(Gitlab::Experimentation::ControllerConcern)
end
describe '#common_invite_modal_dataset' do
context 'when member_areas_of_focus is enabled', :experiment do
context 'with control experience' do
before do
stub_experiments(member_areas_of_focus: :control)
end
it 'has expected attributes' do
attributes = {
areas_of_focus_options: [],
no_selection_areas_of_focus: []
}
expect(helper.common_invite_modal_dataset(project)).to include(attributes)
end
end
context 'with candidate experience' do
before do
stub_experiments(member_areas_of_focus: :candidate)
end
it 'has expected attributes', :aggregate_failures do
output = helper.common_invite_modal_dataset(project)
expect(output[:no_selection_areas_of_focus]).to eq ['no_selection']
expect(Gitlab::Json.parse(output[:areas_of_focus_options]).first['value']).to eq 'Contribute to the codebase'
end
end
end
context 'when member_areas_of_focus is disabled' do
before do
stub_feature_flags(member_areas_of_focus: false)
end
it 'has expected attributes' do
attributes = {
id: project.id,
name: project.name,
default_access_level: Gitlab::Access::GUEST,
areas_of_focus_options: [],
no_selection_areas_of_focus: []
}
expect(helper.common_invite_modal_dataset(project)).to match(attributes)
end
end
end
context 'with project' do
before do
allow(helper).to receive(:current_user) { owner }
......
......@@ -5,7 +5,7 @@ module Spec
module Helpers
module Features
module InviteMembersModalHelper
def invite_member(name, role: 'Guest', expires_at: nil)
def invite_member(name, role: 'Guest', expires_at: nil, area_of_focus: false)
click_on 'Invite members'
page.within '#invite-members-modal' do
......@@ -14,6 +14,7 @@ module Spec
wait_for_requests
click_button name
choose_options(role, expires_at)
choose_area_of_focus if area_of_focus
click_button 'Invite'
......@@ -41,7 +42,14 @@ module Spec
click_button role
end
fill_in 'YYYY-MM-DD', with: expires_at.try(:strftime, '%Y-%m-%d')
fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at
end
def choose_area_of_focus
page.within '[data-testid="area-of-focus-checks"]' do
check 'Contribute to the codebase'
check 'Collaborate on open issues and merge requests'
end
end
end
end
......
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