Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
27b24dba
Commit
27b24dba
authored
Jan 08, 2021
by
Jackie Fraser
Committed by
Illya Klymov
Jan 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add invite by email to modal
parent
5a791545
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
358 additions
and
82 deletions
+358
-82
app/assets/javascripts/api.js
app/assets/javascripts/api.js
+16
-2
app/assets/javascripts/invite_members/components/invite_members_modal.vue
...cripts/invite_members/components/invite_members_modal.vue
+42
-13
app/assets/javascripts/invite_members/components/members_token_select.vue
...cripts/invite_members/components/members_token_select.vue
+20
-9
app/assets/javascripts/invite_members/init_invite_members_modal.js
...s/javascripts/invite_members/init_invite_members_modal.js
+2
-0
app/views/groups/_invite_members_modal.html.haml
app/views/groups/_invite_members_modal.html.haml
+1
-1
app/views/projects/_invite_members_modal.html.haml
app/views/projects/_invite_members_modal.html.haml
+1
-1
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/api_spec.js
spec/frontend/api_spec.js
+88
-0
spec/frontend/invite_members/components/invite_members_modal_spec.js
...nd/invite_members/components/invite_members_modal_spec.js
+165
-48
spec/frontend/invite_members/components/members_token_select_spec.js
...nd/invite_members/components/members_token_select_spec.js
+20
-8
No files found.
app/assets/javascripts/api.js
View file @
27b24dba
...
...
@@ -13,6 +13,7 @@ const Api = {
groupMilestonesPath
:
'
/api/:version/groups/:id/milestones
'
,
subgroupsPath
:
'
/api/:version/groups/:id/subgroups
'
,
namespacesPath
:
'
/api/:version/namespaces.json
'
,
groupInvitationsPath
:
'
/api/:version/groups/:id/invitations
'
,
groupPackagesPath
:
'
/api/:version/groups/:id/packages
'
,
projectPackagesPath
:
'
/api/:version/projects/:id/packages
'
,
projectPackagePath
:
'
/api/:version/projects/:id/packages/:package_id
'
,
...
...
@@ -23,6 +24,7 @@ const Api = {
projectLabelsPath
:
'
/:namespace_path/:project_path/-/labels
'
,
projectFileSchemaPath
:
'
/:namespace_path/:project_path/-/schema/:ref/:filename
'
,
projectUsersPath
:
'
/api/:version/projects/:id/users
'
,
projectInvitationsPath
:
'
/api/:version/projects/:id/invitations
'
,
projectMembersPath
:
'
/api/:version/projects/:id/members
'
,
projectMergeRequestsPath
:
'
/api/:version/projects/:id/merge_requests
'
,
projectMergeRequestPath
:
'
/api/:version/projects/:id/merge_requests/:mrid
'
,
...
...
@@ -127,12 +129,18 @@ const Api = {
});
},
inviteGroupMember
(
id
,
data
)
{
addGroupMembersByUserId
(
id
,
data
)
{
const
url
=
Api
.
buildUrl
(
this
.
groupMembersPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
return
axios
.
post
(
url
,
data
);
},
inviteGroupMembersByEmail
(
id
,
data
)
{
const
url
=
Api
.
buildUrl
(
this
.
groupInvitationsPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
return
axios
.
post
(
url
,
data
);
},
groupMilestones
(
id
,
options
)
{
const
url
=
Api
.
buildUrl
(
this
.
groupMilestonesPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
...
...
@@ -217,12 +225,18 @@ const Api = {
.
then
(({
data
})
=>
data
);
},
inviteProjectMembers
(
id
,
data
)
{
addProjectMembersByUserId
(
id
,
data
)
{
const
url
=
Api
.
buildUrl
(
this
.
projectMembersPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
return
axios
.
post
(
url
,
data
);
},
inviteProjectMembersByEmail
(
id
,
data
)
{
const
url
=
Api
.
buildUrl
(
this
.
projectInvitationsPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
return
axios
.
post
(
url
,
data
);
},
// Return single project
project
(
projectPath
)
{
const
url
=
Api
.
buildUrl
(
Api
.
projectPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
projectPath
));
...
...
app/assets/javascripts/invite_members/components/invite_members_modal.vue
View file @
27b24dba
...
...
@@ -9,6 +9,7 @@ import {
GlButton
,
GlFormInput
,
}
from
'
@gitlab/ui
'
;
import
{
partition
,
isString
}
from
'
lodash
'
;
import
eventHub
from
'
../event_hub
'
;
import
{
s__
,
__
,
sprintf
}
from
'
~/locale
'
;
import
Api
from
'
~/api
'
;
...
...
@@ -58,7 +59,7 @@ export default {
visible
:
true
,
modalId
:
'
invite-members-modal
'
,
selectedAccessLevel
:
this
.
defaultAccessLevel
,
newUsersToInvite
:
''
,
newUsersToInvite
:
[]
,
selectedDate
:
undefined
,
};
},
...
...
@@ -79,13 +80,12 @@ export default {
return
{
onComplete
:
()
=>
{
this
.
selectedAccessLevel
=
this
.
defaultAccessLevel
;
this
.
newUsersToInvite
=
''
;
this
.
newUsersToInvite
=
[]
;
},
};
},
p
ostData
()
{
baseP
ostData
()
{
return
{
user_id
:
this
.
newUsersToInvite
,
access_level
:
this
.
selectedAccessLevel
,
expires_at
:
this
.
selectedDate
,
format
:
'
json
'
,
...
...
@@ -101,6 +101,17 @@ export default {
eventHub
.
$on
(
'
openModal
'
,
this
.
openModal
);
},
methods
:
{
partitionNewUsersToInvite
()
{
const
[
usersToInviteByEmail
,
usersToAddById
]
=
partition
(
this
.
newUsersToInvite
,
(
user
)
=>
isString
(
user
.
id
)
&&
user
.
id
.
includes
(
'
user-defined-token
'
),
);
return
[
usersToInviteByEmail
.
map
((
user
)
=>
user
.
name
).
join
(
'
,
'
),
usersToAddById
.
map
((
user
)
=>
user
.
id
).
join
(
'
,
'
),
];
},
openModal
()
{
this
.
$root
.
$emit
(
'
bv::show::modal
'
,
this
.
modalId
);
},
...
...
@@ -108,7 +119,7 @@ export default {
this
.
$root
.
$emit
(
'
bv::hide::modal
'
,
this
.
modalId
);
},
sendInvite
()
{
this
.
submitForm
(
this
.
postData
);
this
.
submitForm
();
this
.
closeModal
();
},
cancelInvite
()
{
...
...
@@ -120,15 +131,33 @@ export default {
changeSelectedItem
(
item
)
{
this
.
selectedAccessLevel
=
item
;
},
submitForm
(
formData
)
{
if
(
this
.
isProject
)
{
return
Api
.
inviteProjectMembers
(
this
.
id
,
formData
)
.
then
(
this
.
showToastMessageSuccess
)
.
catch
(
this
.
showToastMessageError
);
submitForm
()
{
const
[
usersToInviteByEmail
,
usersToAddById
]
=
this
.
partitionNewUsersToInvite
();
const
promises
=
[];
if
(
usersToInviteByEmail
!==
''
)
{
const
apiInviteByEmail
=
this
.
isProject
?
Api
.
inviteProjectMembersByEmail
.
bind
(
Api
)
:
Api
.
inviteGroupMembersByEmail
.
bind
(
Api
);
promises
.
push
(
apiInviteByEmail
(
this
.
id
,
this
.
inviteByEmailPostData
(
usersToInviteByEmail
)));
}
return
Api
.
inviteGroupMember
(
this
.
id
,
formData
)
.
then
(
this
.
showToastMessageSuccess
)
.
catch
(
this
.
showToastMessageError
);
if
(
usersToAddById
!==
''
)
{
const
apiAddByUserId
=
this
.
isProject
?
Api
.
addProjectMembersByUserId
.
bind
(
Api
)
:
Api
.
addGroupMembersByUserId
.
bind
(
Api
);
promises
.
push
(
apiAddByUserId
(
this
.
id
,
this
.
addByUserIdPostData
(
usersToAddById
)));
}
Promise
.
all
(
promises
).
then
(
this
.
showToastMessageSuccess
).
catch
(
this
.
showToastMessageError
);
},
inviteByEmailPostData
(
usersToInviteByEmail
)
{
return
{
...
this
.
basePostData
,
email
:
usersToInviteByEmail
};
},
addByUserIdPostData
(
usersToAddById
)
{
return
{
...
this
.
basePostData
,
user_id
:
usersToAddById
};
},
showToastMessageSuccess
()
{
this
.
$toast
.
show
(
this
.
$options
.
labels
.
toastMessageSuccessful
,
this
.
toastOptions
);
...
...
app/assets/javascripts/invite_members/components/members_token_select.vue
View file @
27b24dba
<
script
>
import
{
debounce
}
from
'
lodash
'
;
import
{
GlTokenSelector
,
GlAvatar
,
GlAvatarLabeled
}
from
'
@gitlab/ui
'
;
import
{
GlTokenSelector
,
GlAvatar
,
GlAvatarLabeled
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
USER_SEARCH_DELAY
}
from
'
../constants
'
;
import
Api
from
'
~/api
'
;
...
...
@@ -9,6 +10,7 @@ export default {
GlTokenSelector
,
GlAvatar
,
GlAvatarLabeled
,
GlSprintf
,
},
props
:
{
placeholder
:
{
...
...
@@ -32,12 +34,10 @@ export default {
};
},
computed
:
{
newUsersToInvite
()
{
return
this
.
selectedTokens
.
map
((
obj
)
=>
{
return
obj
.
id
;
})
.
join
(
'
,
'
);
emailIsValid
()
{
const
regex
=
/.+@/
;
return
this
.
query
.
match
(
regex
)
!==
null
;
},
placeholderText
()
{
if
(
this
.
selectedTokens
.
length
===
0
)
{
...
...
@@ -69,7 +69,7 @@ export default {
});
},
USER_SEARCH_DELAY
),
handleInput
()
{
this
.
$emit
(
'
input
'
,
this
.
newUsersToInvite
);
this
.
$emit
(
'
input
'
,
this
.
selectedTokens
);
},
handleBlur
()
{
this
.
hideDropdownWithNoItems
=
false
;
...
...
@@ -86,6 +86,9 @@ export default {
},
},
queryOptions
:
{
exclude_internal
:
true
,
active
:
true
},
i18n
:
{
inviteTextMessage
:
__
(
'
Invite "%{email}" by email
'
),
},
};
</
script
>
...
...
@@ -94,7 +97,7 @@ export default {
v-model=
"selectedTokens"
:dropdown-items=
"users"
:loading=
"loading"
:allow-user-defined-tokens=
"
false
"
:allow-user-defined-tokens=
"
emailIsValid
"
:hide-dropdown-with-no-items=
"hideDropdownWithNoItems"
:placeholder=
"placeholderText"
:aria-labelledby=
"ariaLabelledby"
...
...
@@ -116,5 +119,13 @@ export default {
:sub-label=
"dropdownItem.username"
/>
</
template
>
<
template
#user-defined-token-content=
"{ inputText: email }"
>
<gl-sprintf
:message=
"$options.i18n.inviteTextMessage"
>
<template
#email
>
<span>
{{
email
}}
</span>
</
template
>
</gl-sprintf>
</template>
</gl-token-selector>
</template>
app/assets/javascripts/invite_members/init_invite_members_modal.js
View file @
27b24dba
import
Vue
from
'
vue
'
;
import
{
GlToast
}
from
'
@gitlab/ui
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
InviteMembersModal
from
'
~/invite_members/components/invite_members_modal.vue
'
;
Vue
.
use
(
GlToast
);
...
...
@@ -17,6 +18,7 @@ export default function initInviteMembersModal() {
createElement
(
InviteMembersModal
,
{
props
:
{
...
el
.
dataset
,
isProject
:
parseBoolean
(
el
.
dataset
.
isProject
),
accessLevels
:
JSON
.
parse
(
el
.
dataset
.
accessLevels
),
},
}),
...
...
app/views/groups/_invite_members_modal.html.haml
View file @
27b24dba
-
if
invite_members_allowed?
(
group
)
.js-invite-members-modal
{
data:
{
id:
group
.
id
,
name:
group
.
name
,
is_project:
false
,
is_project:
'false'
,
access_levels:
GroupMember
.
access_level_roles
.
to_json
,
default_access_level:
Gitlab
::
Access
::
GUEST
,
help_link:
help_page_url
(
'user/permissions'
)
}
}
app/views/projects/_invite_members_modal.html.haml
View file @
27b24dba
-
if
invite_members_allowed?
(
project
.
group
)
.js-invite-members-modal
{
data:
{
id:
project
.
id
,
name:
project
.
name
,
is_project:
true
,
is_project:
'true'
,
access_levels:
GroupMember
.
access_level_roles
.
to_json
,
default_access_level:
Gitlab
::
Access
::
GUEST
,
help_link:
help_page_url
(
'user/permissions'
)
}
}
locale/gitlab.pot
View file @
27b24dba
...
...
@@ -15342,6 +15342,9 @@ msgstr ""
msgid "Invite"
msgstr ""
msgid "Invite \"%{email}\" by email"
msgstr ""
msgid "Invite \"%{trimmed}\" by email"
msgstr ""
...
...
spec/frontend/api_spec.js
View file @
27b24dba
...
...
@@ -167,6 +167,50 @@ describe('Api', () => {
});
});
describe
(
'
addGroupMembersByUserId
'
,
()
=>
{
it
(
'
adds an existing User as a new Group Member by User ID
'
,
()
=>
{
const
groupId
=
1
;
const
expectedUserId
=
2
;
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/groups/1/members`
;
const
params
=
{
user_id
:
expectedUserId
,
access_level
:
10
,
expires_at
:
undefined
,
};
mock
.
onPost
(
expectedUrl
).
reply
(
200
,
{
id
:
expectedUserId
,
state
:
'
active
'
,
});
return
Api
.
addGroupMembersByUserId
(
groupId
,
params
).
then
(({
data
})
=>
{
expect
(
data
.
id
).
toBe
(
expectedUserId
);
expect
(
data
.
state
).
toBe
(
'
active
'
);
});
});
});
describe
(
'
inviteGroupMembersByEmail
'
,
()
=>
{
it
(
'
invites a new email address to create a new User and become a Group Member
'
,
()
=>
{
const
groupId
=
1
;
const
email
=
'
email@example.com
'
;
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/groups/1/invitations`
;
const
params
=
{
email
,
access_level
:
10
,
expires_at
:
undefined
,
};
mock
.
onPost
(
expectedUrl
).
reply
(
200
,
{
status
:
'
success
'
,
});
return
Api
.
inviteGroupMembersByEmail
(
groupId
,
params
).
then
(({
data
})
=>
{
expect
(
data
.
status
).
toBe
(
'
success
'
);
});
});
});
describe
(
'
groupMilestones
'
,
()
=>
{
it
(
'
fetches group milestones
'
,
(
done
)
=>
{
const
groupId
=
'
16
'
;
...
...
@@ -458,6 +502,50 @@ describe('Api', () => {
});
});
describe
(
'
addProjectMembersByUserId
'
,
()
=>
{
it
(
'
adds an existing User as a new Project Member by User ID
'
,
()
=>
{
const
projectId
=
1
;
const
expectedUserId
=
2
;
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/projects/1/members`
;
const
params
=
{
user_id
:
expectedUserId
,
access_level
:
10
,
expires_at
:
undefined
,
};
mock
.
onPost
(
expectedUrl
).
reply
(
200
,
{
id
:
expectedUserId
,
state
:
'
active
'
,
});
return
Api
.
addProjectMembersByUserId
(
projectId
,
params
).
then
(({
data
})
=>
{
expect
(
data
.
id
).
toBe
(
expectedUserId
);
expect
(
data
.
state
).
toBe
(
'
active
'
);
});
});
});
describe
(
'
inviteProjectMembersByEmail
'
,
()
=>
{
it
(
'
invites a new email address to create a new User and become a Project Member
'
,
()
=>
{
const
projectId
=
1
;
const
expectedEmail
=
'
email@example.com
'
;
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/projects/1/invitations`
;
const
params
=
{
email
:
expectedEmail
,
access_level
:
10
,
expires_at
:
undefined
,
};
mock
.
onPost
(
expectedUrl
).
reply
(
200
,
{
status
:
'
success
'
,
});
return
Api
.
inviteProjectMembersByEmail
(
projectId
,
params
).
then
(({
data
})
=>
{
expect
(
data
.
status
).
toBe
(
'
success
'
);
});
});
});
describe
(
'
newLabel
'
,
()
=>
{
it
(
'
creates a new label
'
,
(
done
)
=>
{
const
namespace
=
'
some namespace
'
;
...
...
spec/frontend/invite_members/components/invite_members_modal_spec.js
View file @
27b24dba
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlDropdown
,
GlDropdownItem
,
GlDatepicker
,
GlSprintf
,
GlLink
,
GlModal
}
from
'
@gitlab/ui
'
;
import
{
stubComponent
}
from
'
helpers/stub_component
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
Api
from
'
~/api
'
;
import
InviteMembersModal
from
'
~/invite_members/components/invite_members_modal.vue
'
;
...
...
@@ -11,6 +12,15 @@ const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, O
const
defaultAccessLevel
=
'
10
'
;
const
helpLink
=
'
https://example.com
'
;
const
user1
=
{
id
:
1
,
name
:
'
Name One
'
,
username
:
'
one_1
'
,
avatar_url
:
''
};
const
user2
=
{
id
:
2
,
name
:
'
Name Two
'
,
username
:
'
one_2
'
,
avatar_url
:
''
};
const
user3
=
{
id
:
'
user-defined-token
'
,
name
:
'
email@example.com
'
,
username
:
'
one_2
'
,
avatar_url
:
''
,
};
const
createComponent
=
(
data
=
{})
=>
{
return
shallowMount
(
InviteMembersModal
,
{
propsData
:
{
...
...
@@ -50,6 +60,7 @@ describe('InviteMembersModal', () => {
const
findLink
=
()
=>
wrapper
.
find
(
GlLink
);
const
findCancelButton
=
()
=>
wrapper
.
find
({
ref
:
'
cancelButton
'
});
const
findInviteButton
=
()
=>
wrapper
.
find
({
ref
:
'
inviteButton
'
});
const
clickInviteButton
=
()
=>
findInviteButton
().
vm
.
$emit
(
'
click
'
);
describe
(
'
rendering the modal
'
,
()
=>
{
beforeEach
(()
=>
{
...
...
@@ -92,78 +103,184 @@ describe('InviteMembersModal', () => {
});
describe
(
'
submitting the invite form
'
,
()
=>
{
const
apiErrorMessage
=
'
Member already exists
'
;
describe
(
'
when inviting an existing user to group by user ID
'
,
()
=>
{
const
postData
=
{
user_id
:
'
1
'
,
access_level
:
'
10
'
,
expires_at
:
new
Date
()
,
expires_at
:
undefined
,
format
:
'
json
'
,
};
describe
(
'
when the invite was
sent successfully
'
,
()
=>
{
describe
(
'
when invites are
sent successfully
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
(
);
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
]
}
);
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
inviteGroupMember
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageSuccess
'
);
wrapper
.
vm
.
submitForm
(
postData
);
clickInviteButton
();
});
it
(
'
calls Api addGroupMembersByUserId with the correct params
'
,
()
=>
{
expect
(
Api
.
addGroupMembersByUserId
).
toHaveBeenCalledWith
(
id
,
postData
);
});
it
(
'
displays the successful toastMessage
'
,
()
=>
{
const
toastMessageSuccessful
=
'
Members were successfully added
'
;
expect
(
wrapper
.
vm
.
showToastMessageSuccess
).
toHaveBeenCalled
();
});
});
describe
(
'
when the invite received an api error message
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
)
.
mockRejectedValue
({
response
:
{
data
:
{
message
:
apiErrorMessage
}
}
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageError
'
);
clickInviteButton
();
});
it
(
'
displays the apiErrorMessage in the toastMessage
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
wrapper
.
vm
.
showToastMessageError
).
toHaveBeenCalledWith
({
response
:
{
data
:
{
message
:
apiErrorMessage
}
},
});
});
});
describe
(
'
when any invite failed for any other reason
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
,
user2
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
)
.
mockRejectedValue
({
response
:
{
data
:
{
success
:
false
}
}
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageError
'
);
expect
(
wrapper
.
vm
.
$toast
.
show
).
toHaveBeenCalledWith
(
toastMessageSuccessful
,
wrapper
.
vm
.
toastOptions
,
);
clickInviteButton
();
});
it
(
'
calls Api inviteGroupMember with the correct params
'
,
()
=>
{
expect
(
Api
.
inviteGroupMember
).
toHaveBeenCalledWith
(
id
,
postData
);
it
(
'
displays the generic error toastMessage
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
wrapper
.
vm
.
showToastMessageError
).
toHaveBeenCalled
();
});
});
});
describe
(
'
when inviting a new user by email address
'
,
()
=>
{
const
postData
=
{
access_level
:
'
10
'
,
expires_at
:
undefined
,
email
:
'
email@example.com
'
,
format
:
'
json
'
,
};
describe
(
'
when invites are sent successfully
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
[
user3
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
inviteGroupMembersByEmail
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageSuccess
'
);
clickInviteButton
();
});
it
(
'
calls Api inviteGroupMembersByEmail with the correct params
'
,
()
=>
{
expect
(
Api
.
inviteGroupMembersByEmail
).
toHaveBeenCalledWith
(
id
,
postData
);
});
describe
(
'
when sending the invite for a single member returned an api error
'
,
()
=>
{
const
apiErrorMessage
=
'
Members already exists
'
;
it
(
'
displays the successful toastMessage
'
,
()
=>
{
expect
(
wrapper
.
vm
.
showToastMessageSuccess
).
toHaveBeenCalled
();
});
});
describe
(
'
when any invite failed for any reason
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
'
123
'
});
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
,
user2
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
inviteGroupMember
'
)
.
mockRejectedValue
({
response
:
{
data
:
{
message
:
apiErrorMessage
}
}
});
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
)
.
mockRejectedValue
({
response
:
{
data
:
{
success
:
false
}
}
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageError
'
);
clickInviteButton
();
});
it
(
'
displays the generic error toastMessage
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
wrapper
.
vm
.
showToastMessageError
).
toHaveBeenCalled
();
});
});
});
findInviteButton
().
vm
.
$emit
(
'
click
'
);
describe
(
'
when inviting members and non-members in same click
'
,
()
=>
{
const
postData
=
{
access_level
:
'
10
'
,
expires_at
:
undefined
,
format
:
'
json
'
,
};
const
emailPostData
=
{
...
postData
,
email
:
'
email@example.com
'
};
const
idPostData
=
{
...
postData
,
user_id
:
'
1
'
};
describe
(
'
when invites are sent successfully
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
,
user3
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
inviteGroupMembersByEmail
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageSuccess
'
);
clickInviteButton
();
});
it
(
'
displays the api error message for the toastMessage
'
,
()
=>
{
expect
(
wrapper
.
vm
.
$toast
.
show
).
toHaveBeenCalledWith
(
apiErrorMessage
,
wrapper
.
vm
.
toastOptions
,
);
it
(
'
calls Api inviteGroupMembersByEmail with the correct params
'
,
()
=>
{
expect
(
Api
.
inviteGroupMembersByEmail
).
toHaveBeenCalledWith
(
id
,
emailPostData
);
});
it
(
'
calls Api addGroupMembersByUserId with the correct params
'
,
()
=>
{
expect
(
Api
.
addGroupMembersByUserId
).
toHaveBeenCalledWith
(
id
,
idPostData
);
});
describe
(
'
when sending the invite for multiple members returned any error
'
,
()
=>
{
const
genericErrorMessage
=
'
Some of the members could not be added
'
;
it
(
'
displays the successful toastMessage
'
,
()
=>
{
expect
(
wrapper
.
vm
.
showToastMessageSuccess
).
toHaveBeenCalled
();
});
});
describe
(
'
when any invite failed for any reason
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
newUsersToInvite
:
'
123
'
});
wrapper
=
createComponent
({
newUsersToInvite
:
[
user1
,
user3
]
});
wrapper
.
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
jest
.
spyOn
(
Api
,
'
inviteGroupMember
'
)
.
spyOn
(
Api
,
'
inviteGroupMembersByEmail
'
)
.
mockRejectedValue
({
response
:
{
data
:
{
success
:
false
}
}
});
findInviteButton
().
vm
.
$emit
(
'
click
'
);
jest
.
spyOn
(
Api
,
'
addGroupMembersByUserId
'
).
mockResolvedValue
({
data
:
postData
});
jest
.
spyOn
(
wrapper
.
vm
,
'
showToastMessageError
'
);
clickInviteButton
();
});
it
(
'
displays the expected toastMessage
'
,
()
=>
{
expect
(
wrapper
.
vm
.
$toast
.
show
).
toHaveBeenCalledWith
(
genericErrorMessage
,
wrapper
.
vm
.
toastOptions
,
);
it
(
'
displays the generic error toastMessage
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
wrapper
.
vm
.
showToastMessageError
).
toHaveBeenCalled
();
}
);
});
});
});
...
...
spec/frontend/invite_members/components/members_token_select_spec.js
View file @
27b24dba
...
...
@@ -8,8 +8,8 @@ import MembersTokenSelect from '~/invite_members/components/members_token_select
const
label
=
'
testgroup
'
;
const
placeholder
=
'
Search for a member
'
;
const
user1
=
{
id
:
1
,
name
:
'
Name One
'
,
username
:
'
one_1
'
,
avatar_url
:
''
};
const
user2
=
{
id
:
2
,
name
:
'
Name Two
'
,
username
:
'
two_2
'
,
avatar_url
:
''
};
const
user1
=
{
id
:
1
,
name
:
'
John Smith
'
,
username
:
'
one_1
'
,
avatar_url
:
''
};
const
user2
=
{
id
:
2
,
name
:
'
Jane Doe
'
,
username
:
'
two_2
'
,
avatar_url
:
''
};
const
allUsers
=
[
user1
,
user2
];
const
createComponent
=
()
=>
{
...
...
@@ -77,9 +77,14 @@ describe('MembersTokenSelect', () => {
});
describe
(
'
when text input is typed in
'
,
()
=>
{
let
tokenSelector
;
beforeEach
(()
=>
{
tokenSelector
=
findTokenSelector
();
});
it
(
'
calls the API with search parameter
'
,
async
()
=>
{
const
searchParam
=
'
One
'
;
const
tokenSelector
=
findTokenSelector
();
tokenSelector
.
vm
.
$emit
(
'
text-input
'
,
searchParam
);
...
...
@@ -88,16 +93,23 @@ describe('MembersTokenSelect', () => {
expect
(
Api
.
users
).
toHaveBeenCalledWith
(
searchParam
,
wrapper
.
vm
.
$options
.
queryOptions
);
expect
(
tokenSelector
.
props
(
'
hideDropdownWithNoItems
'
)).
toBe
(
false
);
});
describe
(
'
when input text is an email
'
,
()
=>
{
it
(
'
allows user defined tokens
'
,
async
()
=>
{
tokenSelector
.
vm
.
$emit
(
'
text-input
'
,
'
foo@bar.com
'
);
await
nextTick
();
expect
(
tokenSelector
.
props
(
'
allowUserDefinedTokens
'
)).
toBe
(
true
);
});
});
});
describe
(
'
when user is selected
'
,
()
=>
{
it
(
'
emits `input` event with selected users
'
,
()
=>
{
findTokenSelector
().
vm
.
$emit
(
'
input
'
,
[
{
id
:
1
,
name
:
'
John Smith
'
},
{
id
:
2
,
name
:
'
Jane Doe
'
},
]);
findTokenSelector
().
vm
.
$emit
(
'
input
'
,
[
user1
,
user2
]);
expect
(
wrapper
.
emitted
().
input
[
0
][
0
]).
to
Be
(
'
1,2
'
);
expect
(
wrapper
.
emitted
().
input
[
0
][
0
]).
to
Equal
([
user1
,
user2
]
);
});
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment