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
e1961ad3
Commit
e1961ad3
authored
Jan 05, 2021
by
Andrei Stoicescu
Committed by
Natalia Tepluhina
Jan 05, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add search box to billable users table
- add search box - modify state to handle filtering - change styling
parent
a5fb595d
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
312 additions
and
90 deletions
+312
-90
app/assets/javascripts/api.js
app/assets/javascripts/api.js
+8
-1
ee/app/assets/javascripts/billings/seat_usage/components/subscription_seats.vue
...pts/billings/seat_usage/components/subscription_seats.vue
+70
-31
ee/app/assets/javascripts/billings/seat_usage/store/actions.js
...p/assets/javascripts/billings/seat_usage/store/actions.js
+6
-2
ee/app/assets/javascripts/billings/seat_usage/store/getters.js
...p/assets/javascripts/billings/seat_usage/store/getters.js
+0
-1
ee/app/assets/javascripts/billings/seat_usage/store/mutation_types.js
...s/javascripts/billings/seat_usage/store/mutation_types.js
+4
-0
ee/app/assets/javascripts/billings/seat_usage/store/mutations.js
...assets/javascripts/billings/seat_usage/store/mutations.js
+14
-0
ee/spec/frontend/billings/seat_usage/components/subscription_seats_spec.js
...billings/seat_usage/components/subscription_seats_spec.js
+103
-6
ee/spec/frontend/billings/seat_usage/store/actions_spec.js
ee/spec/frontend/billings/seat_usage/store/actions_spec.js
+64
-47
ee/spec/frontend/billings/seat_usage/store/mutations_spec.js
ee/spec/frontend/billings/seat_usage/store/mutations_spec.js
+35
-0
locale/gitlab.pot
locale/gitlab.pot
+8
-2
No files found.
app/assets/javascripts/api.js
View file @
e1961ad3
...
...
@@ -836,11 +836,18 @@ const Api = {
page
:
1
,
};
const
passedOptions
=
options
;
// calling search API with empty string will not return results
if
(
!
passedOptions
.
search
)
{
passedOptions
.
search
=
undefined
;
}
return
axios
.
get
(
url
,
{
params
:
{
...
defaults
,
...
o
ptions
,
...
passedO
ptions
,
},
})
.
then
(({
data
,
headers
})
=>
{
...
...
ee/app/assets/javascripts/billings/seat_usage/components/subscription_seats.vue
View file @
e1961ad3
...
...
@@ -5,13 +5,15 @@ import {
GlAvatarLabeled
,
GlAvatarLink
,
GlPagination
,
GlLoadingIcon
,
GlTooltipDirective
,
GlSearchBoxByType
,
GlBadge
,
}
from
'
@gitlab/ui
'
;
import
{
parseInt
}
from
'
lodash
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
{
parseInt
,
debounce
}
from
'
lodash
'
;
import
{
s__
}
from
'
~/locale
'
;
const
AVATAR_SIZE
=
32
;
const
SEARCH_DEBOUNCE_MS
=
250
;
export
default
{
directives
:
{
...
...
@@ -22,31 +24,24 @@ export default {
GlAvatarLabeled
,
GlAvatarLink
,
GlPagination
,
GlLoadingIcon
,
GlSearchBoxByType
,
GlBadge
,
},
data
()
{
return
{
fields
:
[
'
user
'
,
'
email
'
],
searchQuery
:
''
,
};
},
computed
:
{
...
mapState
([
'
isLoading
'
,
'
page
'
,
'
perPage
'
,
'
total
'
,
'
namespace
Id
'
,
'
namespace
Name
'
]),
...
mapState
([
'
isLoading
'
,
'
page
'
,
'
perPage
'
,
'
total
'
,
'
namespaceName
'
]),
...
mapGetters
([
'
tableItems
'
]),
headingText
()
{
return
sprintf
(
s__
(
'
Billing|Users occupying seats in %{namespaceName} Group (%{total})
'
),
{
total
:
this
.
total
,
namespaceName
:
this
.
namespaceName
,
});
},
subHeadingText
()
{
return
s__
(
'
Billing|Updated live
'
);
},
currentPage
:
{
get
()
{
return
parseInt
(
this
.
page
,
10
);
},
set
(
val
)
{
this
.
fetchBillableMembersList
(
val
);
this
.
fetchBillableMembersList
(
{
page
:
val
,
search
:
this
.
searchQuery
}
);
},
},
perPageFormatted
()
{
...
...
@@ -55,14 +50,45 @@ export default {
totalFormatted
()
{
return
parseInt
(
this
.
total
,
10
);
},
emptyText
()
{
if
(
this
.
searchQuery
?.
length
<
3
)
{
return
s__
(
'
Billing|Enter at least three characters to search.
'
);
}
return
s__
(
'
Billing|No users to display.
'
);
},
},
watch
:
{
searchQuery
()
{
this
.
executeQuery
();
},
},
created
()
{
this
.
fetchBillableMembersList
(
1
);
// This method is defined here instead of in `methods`
// because we need to access the .cancel() method
// lodash attaches to the function, which is
// made inaccessible by Vue. More info:
// https://stackoverflow.com/a/52988020/1063392
this
.
debouncedSearch
=
debounce
(
function
search
()
{
this
.
fetchBillableMembersList
({
search
:
this
.
searchQuery
});
},
SEARCH_DEBOUNCE_MS
);
this
.
fetchBillableMembersList
();
},
methods
:
{
...
mapActions
([
'
fetchBillableMembersList
'
]),
inputHandler
(
val
)
{
this
.
fetchBillableMembersList
(
val
);
...
mapActions
([
'
fetchBillableMembersList
'
,
'
resetMembers
'
]),
onSearchEnter
()
{
this
.
debouncedSearch
.
cancel
();
this
.
executeQuery
();
},
executeQuery
()
{
const
queryLength
=
this
.
searchQuery
?.
length
;
const
MIN_SEARCH_LENGTH
=
3
;
if
(
queryLength
===
0
||
queryLength
>=
MIN_SEARCH_LENGTH
)
{
this
.
debouncedSearch
();
}
else
if
(
queryLength
<
MIN_SEARCH_LENGTH
)
{
this
.
resetMembers
();
}
},
},
avatarSize
:
AVATAR_SIZE
,
...
...
@@ -73,9 +99,28 @@ export default {
</
script
>
<
template
>
<div
class=
"gl-pt-4"
>
<h4
data-testid=
"heading"
>
{{
headingText
}}
</h4>
<p>
{{
subHeadingText
}}
</p>
<section>
<div
class=
"gl-bg-gray-10 gl-p-6 gl-display-md-flex gl-justify-content-space-between gl-align-items-center"
>
<div
data-testid=
"heading-info"
>
<h4
data-testid=
"heading-info-text"
class=
"gl-font-base gl-display-inline-block gl-font-weight-normal"
>
{{
s__
(
'
Billing|Users occupying seats in
'
)
}}
<span
class=
"gl-font-weight-bold"
>
{{
namespaceName
}}
{{
s__
(
'
Billing|Group
'
)
}}
</span>
</h4>
<gl-badge>
{{
total
}}
</gl-badge>
</div>
<gl-search-box-by-type
v-model.trim=
"searchQuery"
:placeholder=
"s__('Billing|Type to search')"
@
keydown.enter.prevent=
"onSearchEnter"
/>
</div>
<gl-table
class=
"seats-table"
:items=
"tableItems"
...
...
@@ -83,6 +128,8 @@ export default {
:busy=
"isLoading"
:show-empty=
"true"
data-testid=
"table"
:empty-text=
"emptyText"
thead-class=
"gl-display-none"
>
<template
#cell(user)=
"data"
>
<div
class=
"gl-display-flex"
>
...
...
@@ -109,14 +156,6 @@ export default {
>
</div>
</
template
>
<
template
#empty
>
{{
s__
(
'
Billing|No users to display.
'
)
}}
</
template
>
<
template
#table-busy
>
<gl-loading-icon
size=
"lg"
color=
"dark"
class=
"gl-mt-5"
/>
</
template
>
</gl-table>
<gl-pagination
...
...
@@ -127,5 +166,5 @@ export default {
align=
"center"
class=
"gl-mt-5"
/>
</
div
>
</
section
>
</template>
ee/app/assets/javascripts/billings/seat_usage/store/actions.js
View file @
e1961ad3
...
...
@@ -3,10 +3,10 @@ import * as types from './mutation_types';
import
createFlash
from
'
~/flash
'
;
import
{
s__
}
from
'
~/locale
'
;
export
const
fetchBillableMembersList
=
({
dispatch
,
state
},
page
)
=>
{
export
const
fetchBillableMembersList
=
({
dispatch
,
state
},
{
page
,
search
}
=
{}
)
=>
{
dispatch
(
'
requestBillableMembersList
'
);
return
Api
.
fetchBillableGroupMembersList
(
state
.
namespaceId
,
{
page
})
return
Api
.
fetchBillableGroupMembersList
(
state
.
namespaceId
,
{
page
,
search
})
.
then
((
data
)
=>
dispatch
(
'
receiveBillableMembersListSuccess
'
,
data
))
.
catch
(()
=>
dispatch
(
'
receiveBillableMembersListError
'
));
};
...
...
@@ -22,3 +22,7 @@ export const receiveBillableMembersListError = ({ commit }) => {
});
commit
(
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
);
};
export
const
resetMembers
=
({
commit
})
=>
{
commit
(
types
.
RESET_MEMBERS
);
};
ee/app/assets/javascripts/billings/seat_usage/store/getters.js
View file @
e1961ad3
...
...
@@ -6,6 +6,5 @@ export const tableItems = (state) => {
return
{
user
:
{
name
,
username
:
formattedUserName
,
avatar_url
,
web_url
},
email
};
});
}
return
[];
};
ee/app/assets/javascripts/billings/seat_usage/store/mutation_types.js
View file @
e1961ad3
export
const
REQUEST_BILLABLE_MEMBERS
=
'
REQUEST_BILLABLE_MEMBERS
'
;
export
const
RECEIVE_BILLABLE_MEMBERS_SUCCESS
=
'
RECEIVE_BILLABLE_MEMBERS_SUCCESS
'
;
export
const
RECEIVE_BILLABLE_MEMBERS_ERROR
=
'
RECEIVE_BILLABLE_MEMBERS_ERROR
'
;
export
const
SET_SEARCH
=
'
SET_SEARCH
'
;
export
const
RESET_MEMBERS
=
'
RESET_MEMBERS
'
;
ee/app/assets/javascripts/billings/seat_usage/store/mutations.js
View file @
e1961ad3
...
...
@@ -26,4 +26,18 @@ export default {
state
.
isLoading
=
false
;
state
.
hasError
=
true
;
},
[
types
.
SET_SEARCH
](
state
,
searchString
)
{
state
.
search
=
searchString
??
''
;
},
[
types
.
RESET_MEMBERS
](
state
)
{
state
.
members
=
[];
state
.
total
=
null
;
state
.
page
=
null
;
state
.
perPage
=
null
;
state
.
isLoading
=
false
;
},
};
ee/spec/frontend/billings/seat_usage/components/subscription_seats_spec.js
View file @
e1961ad3
import
{
GlPagination
,
GlTable
,
GlAvatarLink
,
GlAvatarLabeled
}
from
'
@gitlab/ui
'
;
import
{
GlPagination
,
GlTable
,
GlAvatarLink
,
GlAvatarLabeled
,
GlSearchBoxByType
,
GlBadge
,
}
from
'
@gitlab/ui
'
;
import
{
mount
,
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
SubscriptionSeats
from
'
ee/billings/seat_usage/components/subscription_seats.vue
'
;
...
...
@@ -8,8 +15,8 @@ const localVue = createLocalVue();
localVue
.
use
(
Vuex
);
const
actionSpies
=
{
setNamespaceId
:
jest
.
fn
(),
fetchBillableMembersList
:
jest
.
fn
(),
resetMembers
:
jest
.
fn
(),
};
const
providedFields
=
{
...
...
@@ -52,7 +59,13 @@ describe('Subscription Seats', () => {
};
const
findTable
=
()
=>
wrapper
.
find
(
GlTable
);
const
findPageHeading
=
()
=>
wrapper
.
find
(
'
[data-testid="heading"]
'
);
const
findTableEmptyText
=
()
=>
findTable
().
attributes
(
'
empty-text
'
);
const
findPageHeading
=
()
=>
wrapper
.
find
(
'
[data-testid="heading-info"]
'
);
const
findPageHeadingText
=
()
=>
findPageHeading
().
find
(
'
[data-testid="heading-info-text"]
'
);
const
findPageHeadingBadge
=
()
=>
findPageHeading
().
find
(
GlBadge
);
const
findSearchBox
=
()
=>
wrapper
.
find
(
GlSearchBoxByType
);
const
findPagination
=
()
=>
wrapper
.
find
(
GlPagination
);
const
serializeUser
=
(
rowWrapper
)
=>
{
...
...
@@ -97,7 +110,7 @@ describe('Subscription Seats', () => {
});
it
(
'
correct actions are called on create
'
,
()
=>
{
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalled
With
(
expect
.
any
(
Object
),
1
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalled
(
);
});
});
...
...
@@ -118,8 +131,8 @@ describe('Subscription Seats', () => {
describe
(
'
heading text
'
,
()
=>
{
it
(
'
contains the group name and total seats number
'
,
()
=>
{
expect
(
findPageHeading
().
text
()).
toMatch
(
providedFields
.
namespaceName
);
expect
(
findPageHeading
().
text
()).
toMatch
(
'
300
'
);
expect
(
findPageHeading
Text
().
text
()).
toMatch
(
providedFields
.
namespaceName
);
expect
(
findPageHeading
Badge
().
text
()).
toMatch
(
'
300
'
);
});
});
...
...
@@ -169,4 +182,88 @@ describe('Subscription Seats', () => {
expect
(
findTable
().
attributes
(
'
busy
'
)).
toBe
(
'
true
'
);
});
});
describe
(
'
search box
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
();
});
it
(
'
input event triggers the fetchBillableMembersList action
'
,
async
()
=>
{
const
SEARCH_STRING
=
'
search string
'
;
// fetchBillableMembersList is called once on created()
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
SEARCH_STRING
);
// fetchBillableMembersList is triggered a second time on input
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
2
);
// fetchBillableMembersList is triggered the second time with the correct argument
expect
(
actionSpies
.
fetchBillableMembersList
.
mock
.
calls
[
1
][
1
]).
toEqual
({
search
:
SEARCH_STRING
,
});
});
});
describe
(
'
typing inside of the search box
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
();
});
it
(
'
causes the empty table text to change based on the number of typed characters
'
,
async
()
=>
{
const
EMPTY_TEXT_TOO_SHORT
=
'
Enter at least three characters to search.
'
;
const
EMPTY_TEXT_NO_USERS
=
'
No users to display.
'
;
expect
(
findTableEmptyText
()).
toBe
(
EMPTY_TEXT_TOO_SHORT
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
a
'
);
expect
(
findTableEmptyText
()).
toBe
(
EMPTY_TEXT_TOO_SHORT
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aa
'
);
expect
(
findTableEmptyText
()).
toBe
(
EMPTY_TEXT_TOO_SHORT
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aaa
'
);
expect
(
findTableEmptyText
()).
toBe
(
EMPTY_TEXT_NO_USERS
);
});
it
(
'
dispatches the resetMembers action when 1 or 2 characters have been typed
'
,
async
()
=>
{
expect
(
actionSpies
.
resetMembers
).
not
.
toHaveBeenCalled
();
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
a
'
);
expect
(
actionSpies
.
resetMembers
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aa
'
);
expect
(
actionSpies
.
resetMembers
).
toHaveBeenCalledTimes
(
2
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aaa
'
);
expect
(
actionSpies
.
resetMembers
).
toHaveBeenCalledTimes
(
2
);
});
it
(
'
dispatches fetchBillableMembersList action when search box is emptied out
'
,
async
()
=>
{
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
a
'
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
''
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
2
);
});
it
(
'
dispatches fetchBillableMembersList action when more than 2 characters are typed
'
,
async
()
=>
{
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
a
'
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aa
'
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
1
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aaa
'
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
2
);
await
findSearchBox
().
vm
.
$emit
(
'
input
'
,
'
aaaa
'
);
expect
(
actionSpies
.
fetchBillableMembersList
).
toHaveBeenCalledTimes
(
3
);
});
});
});
ee/spec/frontend/billings/seat_usage/store/actions_spec.js
View file @
e1961ad3
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
s
tate
from
'
ee/billings/seat_usage/store/state
'
;
import
S
tate
from
'
ee/billings/seat_usage/store/state
'
;
import
*
as
types
from
'
ee/billings/seat_usage/store/mutation_types
'
;
import
*
as
actions
from
'
ee/billings/seat_usage/store/actions
'
;
import
{
mockDataSeats
}
from
'
ee_jest/billings/mock_data
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
createFlash
from
'
~/flash
'
;
import
Api
from
'
~/api
'
;
jest
.
mock
(
'
~/flash
'
);
describe
(
'
seats actions
'
,
()
=>
{
let
mockedS
tate
;
let
s
tate
;
let
mock
;
beforeEach
(()
=>
{
mockedState
=
s
tate
();
state
=
S
tate
();
mock
=
new
MockAdapter
(
axios
);
});
...
...
@@ -26,7 +27,24 @@ describe('seats actions', () => {
describe
(
'
fetchBillableMembersList
'
,
()
=>
{
beforeEach
(()
=>
{
gon
.
api_version
=
'
v4
'
;
mockedState
.
namespaceId
=
1
;
state
.
namespaceId
=
1
;
});
it
(
'
passes correct arguments to Api call
'
,
()
=>
{
const
payload
=
{
page
:
5
,
search
:
'
search string
'
};
const
spy
=
jest
.
spyOn
(
Api
,
'
fetchBillableGroupMembersList
'
);
testAction
({
action
:
actions
.
fetchBillableMembersList
,
payload
,
state
,
expectedMutations
:
expect
.
anything
(),
expectedActions
:
expect
.
anything
(),
});
expect
(
spy
).
toBeCalledWith
(
state
.
namespaceId
,
expect
.
objectContaining
(
payload
));
spy
.
mockRestore
();
});
describe
(
'
on success
'
,
()
=>
{
...
...
@@ -37,19 +55,17 @@ describe('seats actions', () => {
});
it
(
'
should dispatch the request and success actions
'
,
()
=>
{
testAction
(
actions
.
fetchBillableMembersList
,
{},
mockedState
,
[],
[
testAction
({
action
:
actions
.
fetchBillableMembersList
,
state
,
expectedActions
:
[
{
type
:
'
requestBillableMembersList
'
},
{
type
:
'
receiveBillableMembersListSuccess
'
,
payload
:
mockDataSeats
,
},
],
);
}
);
});
});
...
...
@@ -59,59 +75,60 @@ describe('seats actions', () => {
});
it
(
'
should dispatch the request and error actions
'
,
()
=>
{
testAction
(
actions
.
fetchBillableMembersList
,
{},
mockedState
,
[],
[{
type
:
'
requestBillableMembersList
'
},
{
type
:
'
receiveBillableMembersListError
'
}],
);
testAction
({
action
:
actions
.
fetchBillableMembersList
,
state
,
expectedActions
:
[
{
type
:
'
requestBillableMembersList
'
},
{
type
:
'
receiveBillableMembersListError
'
},
],
});
});
});
});
describe
(
'
requestBillableMembersList
'
,
()
=>
{
it
(
'
should commit the request mutation
'
,
()
=>
{
testAction
(
actions
.
requestBillableMembersList
,
{},
testAction
({
action
:
actions
.
requestBillableMembersList
,
state
,
[{
type
:
types
.
REQUEST_BILLABLE_MEMBERS
}],
[],
);
expectedMutations
:
[{
type
:
types
.
REQUEST_BILLABLE_MEMBERS
}],
});
});
});
describe
(
'
receiveBillableMembersListSuccess
'
,
()
=>
{
it
(
'
should commit the success mutation
'
,
()
=>
{
testAction
(
actions
.
receiveBillableMembersListSuccess
,
mockDataSeats
,
mockedState
,
[
{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
,
payload
:
mockDataSeats
,
},
testAction
({
action
:
actions
.
receiveBillableMembersListSuccess
,
payload
:
mockDataSeats
,
state
,
expectedMutations
:
[
{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
,
payload
:
mockDataSeats
},
],
[],
);
});
});
});
describe
(
'
receiveBillableMembersListError
'
,
()
=>
{
it
(
'
should commit the error mutation
'
,
(
done
)
=>
{
testAction
(
actions
.
receiveBillableMembersListError
,
{},
mockedState
,
[{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
}],
[],
()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
done
();
},
);
it
(
'
should commit the error mutation
'
,
async
()
=>
{
await
testAction
({
action
:
actions
.
receiveBillableMembersListError
,
state
,
expectedMutations
:
[{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
}],
});
expect
(
createFlash
).
toHaveBeenCalled
();
});
});
describe
(
'
resetMembers
'
,
()
=>
{
it
(
'
should commit mutation
'
,
()
=>
{
testAction
({
action
:
actions
.
resetMembers
,
state
,
expectedMutations
:
[{
type
:
types
.
RESET_MEMBERS
}],
});
});
});
});
ee/spec/frontend/billings/seat_usage/store/mutations_spec.js
View file @
e1961ad3
...
...
@@ -30,6 +30,8 @@ describe('EE billings seats module mutations', () => {
});
it
(
'
sets state as expected
'
,
()
=>
{
expect
(
state
.
members
).
toMatchObject
(
mockDataSeats
.
data
);
expect
(
state
.
total
).
toBe
(
'
3
'
);
expect
(
state
.
page
).
toBe
(
'
1
'
);
expect
(
state
.
perPage
).
toBe
(
'
1
'
);
...
...
@@ -53,4 +55,37 @@ describe('EE billings seats module mutations', () => {
expect
(
state
.
hasError
).
toBeTruthy
();
});
});
describe
(
types
.
SET_SEARCH
,
()
=>
{
const
SEARCH_STRING
=
'
a search string
'
;
beforeEach
(()
=>
{
mutations
[
types
.
SET_SEARCH
](
state
,
SEARCH_STRING
);
});
it
(
'
sets the search state
'
,
()
=>
{
expect
(
state
.
search
).
toBe
(
SEARCH_STRING
);
});
});
describe
(
types
.
RESET_MEMBERS
,
()
=>
{
beforeEach
(()
=>
{
mutations
[
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
](
state
,
mockDataSeats
);
mutations
[
types
.
RESET_MEMBERS
](
state
);
});
it
(
'
resets members state
'
,
()
=>
{
expect
(
state
.
members
).
toMatchObject
([]);
expect
(
state
.
total
).
toBeNull
();
expect
(
state
.
page
).
toBeNull
();
expect
(
state
.
perPage
).
toBeNull
();
expect
(
state
.
isLoading
).
toBeFalsy
();
});
it
(
'
sets isLoading to false
'
,
()
=>
{
expect
(
state
.
isLoading
).
toBeFalsy
();
});
});
});
locale/gitlab.pot
View file @
e1961ad3
...
...
@@ -4487,16 +4487,22 @@ msgstr ""
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|Enter at least three characters to search."
msgstr ""
msgid "Billing|Group"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
msgid "Billing|Private"
msgstr ""
msgid "Billing|
Updated live
"
msgid "Billing|
Type to search
"
msgstr ""
msgid "Billing|Users occupying seats in
%{namespaceName} Group (%{total})
"
msgid "Billing|Users occupying seats in"
msgstr ""
msgid "Bitbucket Server Import"
...
...
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