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
2795812e
Commit
2795812e
authored
Apr 29, 2021
by
Coung Ngo
Committed by
Jan Provaznik
Apr 29, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add iteration and weight filter token to issues list refactor
parent
ad2cb86b
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
432 additions
and
20 deletions
+432
-20
app/assets/javascripts/issues_list/components/issues_list_app.vue
...ts/javascripts/issues_list/components/issues_list_app.vue
+36
-1
app/assets/javascripts/issues_list/constants.js
app/assets/javascripts/issues_list/constants.js
+20
-0
app/assets/javascripts/issues_list/index.js
app/assets/javascripts/issues_list/index.js
+2
-0
app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
...ts/vue_shared/components/filtered_search_bar/constants.js
+9
-7
app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue
...components/filtered_search_bar/tokens/iteration_token.vue
+110
-0
app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
...ed/components/filtered_search_bar/tokens/weight_token.vue
+58
-0
ee/app/helpers/ee/issues_helper.rb
ee/app/helpers/ee/issues_helper.rb
+7
-1
ee/spec/helpers/ee/issues_helper_spec.rb
ee/spec/helpers/ee/issues_helper_spec.rb
+37
-11
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/issues_list/mock_data.js
spec/frontend/issues_list/mock_data.js
+16
-0
spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
...nd/vue_shared/components/filtered_search_bar/mock_data.js
+19
-0
spec/frontend/vue_shared/components/filtered_search_bar/tokens/iteration_token_spec.js
...onents/filtered_search_bar/tokens/iteration_token_spec.js
+78
-0
spec/frontend/vue_shared/components/filtered_search_bar/tokens/weight_token_spec.js
...omponents/filtered_search_bar/tokens/weight_token_spec.js
+37
-0
No files found.
app/assets/javascripts/issues_list/components/issues_list_app.vue
View file @
2795812e
...
@@ -36,8 +36,10 @@ import { convertObjectPropsToCamelCase, getParameterByName } from '~/lib/utils/c
...
@@ -36,8 +36,10 @@ import { convertObjectPropsToCamelCase, getParameterByName } from '~/lib/utils/c
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
AuthorToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/author_token.vue
'
;
import
AuthorToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/author_token.vue
'
;
import
EmojiToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
'
;
import
EmojiToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
'
;
import
IterationToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue
'
;
import
LabelToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/label_token.vue
'
;
import
LabelToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/label_token.vue
'
;
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
WeightToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
'
;
import
eventHub
from
'
../eventhub
'
;
import
eventHub
from
'
../eventhub
'
;
import
IssueCardTimeInfo
from
'
./issue_card_time_info.vue
'
;
import
IssueCardTimeInfo
from
'
./issue_card_time_info.vue
'
;
...
@@ -88,6 +90,9 @@ export default {
...
@@ -88,6 +90,9 @@ export default {
hasIssues
:
{
hasIssues
:
{
default
:
false
,
default
:
false
,
},
},
hasIssueWeightsFeature
:
{
default
:
false
,
},
initialEmail
:
{
initialEmail
:
{
default
:
''
,
default
:
''
,
},
},
...
@@ -103,6 +108,9 @@ export default {
...
@@ -103,6 +108,9 @@ export default {
newIssuePath
:
{
newIssuePath
:
{
default
:
''
,
default
:
''
,
},
},
projectIterationsPath
:
{
default
:
''
,
},
projectLabelsPath
:
{
projectLabelsPath
:
{
default
:
''
,
default
:
''
,
},
},
...
@@ -155,7 +163,7 @@ export default {
...
@@ -155,7 +163,7 @@ export default {
return
convertToSearchQuery
(
this
.
filterTokens
)
||
undefined
;
return
convertToSearchQuery
(
this
.
filterTokens
)
||
undefined
;
},
},
searchTokens
()
{
searchTokens
()
{
return
[
const
tokens
=
[
{
{
type
:
'
author_username
'
,
type
:
'
author_username
'
,
title
:
__
(
'
Author
'
),
title
:
__
(
'
Author
'
),
...
@@ -216,6 +224,30 @@ export default {
...
@@ -216,6 +224,30 @@ export default {
],
],
},
},
];
];
if
(
this
.
projectIterationsPath
)
{
tokens
.
push
({
type
:
'
iteration
'
,
title
:
__
(
'
Iteration
'
),
icon
:
'
iteration
'
,
token
:
IterationToken
,
unique
:
true
,
defaultIterations
:
[],
fetchIterations
:
this
.
fetchIterations
,
});
}
if
(
this
.
hasIssueWeightsFeature
)
{
tokens
.
push
({
type
:
'
weight
'
,
title
:
__
(
'
Weight
'
),
icon
:
'
weight
'
,
token
:
WeightToken
,
unique
:
true
,
});
}
return
tokens
;
},
},
showPaginationControls
()
{
showPaginationControls
()
{
return
this
.
issues
.
length
>
0
;
return
this
.
issues
.
length
>
0
;
...
@@ -273,6 +305,9 @@ export default {
...
@@ -273,6 +305,9 @@ export default {
fetchMilestones
(
search
)
{
fetchMilestones
(
search
)
{
return
this
.
fetchWithCache
(
this
.
projectMilestonesPath
,
'
milestones
'
,
'
title
'
,
search
,
true
);
return
this
.
fetchWithCache
(
this
.
projectMilestonesPath
,
'
milestones
'
,
'
title
'
,
search
,
true
);
},
},
fetchIterations
(
search
)
{
return
axios
.
get
(
this
.
projectIterationsPath
,
{
params
:
{
search
}
});
},
fetchUsers
(
search
)
{
fetchUsers
(
search
)
{
return
axios
.
get
(
this
.
autocompleteUsersPath
,
{
params
:
{
search
}
});
return
axios
.
get
(
this
.
autocompleteUsersPath
,
{
params
:
{
search
}
});
},
},
...
...
app/assets/javascripts/issues_list/constants.js
View file @
2795812e
...
@@ -334,4 +334,24 @@ export const filters = {
...
@@ -334,4 +334,24 @@ export const filters = {
[
OPERATOR_IS
]:
'
confidential
'
,
[
OPERATOR_IS
]:
'
confidential
'
,
},
},
},
},
iteration
:
{
apiParam
:
{
[
OPERATOR_IS
]:
'
iteration_title
'
,
[
OPERATOR_IS_NOT
]:
'
not[iteration_title]
'
,
},
urlParam
:
{
[
OPERATOR_IS
]:
'
iteration_title
'
,
[
OPERATOR_IS_NOT
]:
'
not[iteration_title]
'
,
},
},
weight
:
{
apiParam
:
{
[
OPERATOR_IS
]:
'
weight
'
,
[
OPERATOR_IS_NOT
]:
'
not[weight]
'
,
},
urlParam
:
{
[
OPERATOR_IS
]:
'
weight
'
,
[
OPERATOR_IS_NOT
]:
'
not[weight]
'
,
},
},
};
};
app/assets/javascripts/issues_list/index.js
View file @
2795812e
...
@@ -98,6 +98,7 @@ export function initIssuesListApp() {
...
@@ -98,6 +98,7 @@ export function initIssuesListApp() {
maxAttachmentSize
,
maxAttachmentSize
,
newIssuePath
,
newIssuePath
,
projectImportJiraPath
,
projectImportJiraPath
,
projectIterationsPath
,
projectLabelsPath
,
projectLabelsPath
,
projectMilestonesPath
,
projectMilestonesPath
,
projectPath
,
projectPath
,
...
@@ -128,6 +129,7 @@ export function initIssuesListApp() {
...
@@ -128,6 +129,7 @@ export function initIssuesListApp() {
issuesPath
,
issuesPath
,
jiraIntegrationPath
,
jiraIntegrationPath
,
newIssuePath
,
newIssuePath
,
projectIterationsPath
,
projectLabelsPath
,
projectLabelsPath
,
projectMilestonesPath
,
projectMilestonesPath
,
projectPath
,
projectPath
,
...
...
app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
View file @
2795812e
/* eslint-disable @gitlab/require-i18n-strings */
/* eslint-disable @gitlab/require-i18n-strings */
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
export
const
DEBOUNCE_DELAY
=
200
;
const
DEFAULT_LABEL_NO_LABEL
=
{
value
:
'
No label
'
,
text
:
__
(
'
No label
'
)
};
const
DEFAULT_LABEL_NO_LABEL
=
{
value
:
'
No label
'
,
text
:
__
(
'
No label
'
)
};
export
const
DEFAULT_LABEL_NONE
=
{
value
:
'
None
'
,
text
:
__
(
'
None
'
)
};
export
const
DEFAULT_LABEL_NONE
=
{
value
:
'
None
'
,
text
:
__
(
'
None
'
)
};
export
const
DEFAULT_LABEL_ANY
=
{
value
:
'
Any
'
,
text
:
__
(
'
Any
'
)
};
export
const
DEFAULT_LABEL_ANY
=
{
value
:
'
Any
'
,
text
:
__
(
'
Any
'
)
};
export
const
DEFAULT_LABEL_CURRENT
=
{
value
:
'
Current
'
,
text
:
__
(
'
Current
'
)
};
export
const
DEFAULT_
LABELS
=
[
DEFAULT_LABEL_NO_LABEL
];
export
const
DEFAULT_
ITERATIONS
=
[
DEFAULT_LABEL_NONE
,
DEFAULT_LABEL_ANY
,
DEFAULT_LABEL_CURRENT
];
export
const
DEBOUNCE_DELAY
=
200
;
export
const
DEFAULT_LABELS
=
[
DEFAULT_LABEL_NO_LABEL
];
export
const
SortDirection
=
{
descending
:
'
descending
'
,
ascending
:
'
ascending
'
,
};
export
const
DEFAULT_MILESTONES
=
[
export
const
DEFAULT_MILESTONES
=
[
DEFAULT_LABEL_NONE
,
DEFAULT_LABEL_NONE
,
...
@@ -21,4 +19,8 @@ export const DEFAULT_MILESTONES = [
...
@@ -21,4 +19,8 @@ export const DEFAULT_MILESTONES = [
{
value
:
'
Started
'
,
text
:
__
(
'
Started
'
)
},
{
value
:
'
Started
'
,
text
:
__
(
'
Started
'
)
},
];
];
export
const
SortDirection
=
{
descending
:
'
descending
'
,
ascending
:
'
ascending
'
,
};
/* eslint-enable @gitlab/require-i18n-strings */
/* eslint-enable @gitlab/require-i18n-strings */
app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue
0 → 100644
View file @
2795812e
<
script
>
import
{
GlDropdownDivider
,
GlFilteredSearchSuggestion
,
GlFilteredSearchToken
,
GlLoadingIcon
,
}
from
'
@gitlab/ui
'
;
import
{
debounce
}
from
'
lodash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
DEBOUNCE_DELAY
,
DEFAULT_ITERATIONS
}
from
'
../constants
'
;
export
default
{
components
:
{
GlDropdownDivider
,
GlFilteredSearchSuggestion
,
GlFilteredSearchToken
,
GlLoadingIcon
,
},
props
:
{
config
:
{
type
:
Object
,
required
:
true
,
},
value
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
iterations
:
this
.
config
.
initialIterations
||
[],
defaultIterations
:
this
.
config
.
defaultIterations
||
DEFAULT_ITERATIONS
,
loading
:
true
,
};
},
computed
:
{
currentValue
()
{
return
this
.
value
.
data
;
},
activeIteration
()
{
return
this
.
iterations
.
find
((
iteration
)
=>
iteration
.
title
===
this
.
currentValue
);
},
},
watch
:
{
active
:
{
immediate
:
true
,
handler
(
newValue
)
{
if
(
!
newValue
&&
!
this
.
iterations
.
length
)
{
this
.
fetchIterationBySearchTerm
(
this
.
currentValue
);
}
},
},
},
methods
:
{
fetchIterationBySearchTerm
(
searchTerm
)
{
const
fetchPromise
=
this
.
config
.
fetchPath
?
this
.
config
.
fetchIterations
(
this
.
config
.
fetchPath
,
searchTerm
)
:
this
.
config
.
fetchIterations
(
searchTerm
);
this
.
loading
=
true
;
fetchPromise
.
then
((
response
)
=>
{
this
.
iterations
=
Array
.
isArray
(
response
)
?
response
:
response
.
data
;
})
.
catch
(()
=>
createFlash
({
message
:
__
(
'
There was a problem fetching iterations.
'
)
}))
.
finally
(()
=>
{
this
.
loading
=
false
;
});
},
searchIterations
:
debounce
(
function
debouncedSearch
({
data
})
{
this
.
fetchIterationBySearchTerm
(
data
);
},
DEBOUNCE_DELAY
),
},
};
</
script
>
<
template
>
<gl-filtered-search-token
:config=
"config"
v-bind=
"
{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchIterations"
>
<template
#view
="
{ inputValue }">
{{
activeIteration
?
activeIteration
.
title
:
inputValue
}}
</
template
>
<
template
#suggestions
>
<gl-filtered-search-suggestion
v-for=
"iteration in defaultIterations"
:key=
"iteration.value"
:value=
"iteration.value"
>
{{
iteration
.
text
}}
</gl-filtered-search-suggestion>
<gl-dropdown-divider
v-if=
"defaultIterations.length"
/>
<gl-loading-icon
v-if=
"loading"
/>
<template
v-else
>
<gl-filtered-search-suggestion
v-for=
"iteration in iterations"
:key=
"iteration.title"
:value=
"iteration.title"
>
{{
iteration
.
title
}}
</gl-filtered-search-suggestion>
</
template
>
</template>
</gl-filtered-search-token>
</template>
app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
0 → 100644
View file @
2795812e
<
script
>
import
{
GlDropdownDivider
,
GlFilteredSearchSuggestion
,
GlFilteredSearchToken
}
from
'
@gitlab/ui
'
;
import
{
DEFAULT_LABEL_ANY
,
DEFAULT_LABEL_NONE
}
from
'
../constants
'
;
export
default
{
baseWeights
:
[
'
0
'
,
'
1
'
,
'
2
'
,
'
3
'
,
'
4
'
,
'
5
'
],
components
:
{
GlDropdownDivider
,
GlFilteredSearchSuggestion
,
GlFilteredSearchToken
,
},
props
:
{
config
:
{
type
:
Object
,
required
:
true
,
},
value
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
weights
:
this
.
$options
.
baseWeights
,
defaultWeights
:
this
.
config
.
defaultWeights
||
[
DEFAULT_LABEL_NONE
,
DEFAULT_LABEL_ANY
],
};
},
methods
:
{
updateWeights
({
data
})
{
const
weight
=
parseInt
(
data
,
10
);
this
.
weights
=
Number
.
isNaN
(
weight
)
?
this
.
$options
.
baseWeights
:
[
String
(
weight
)];
},
},
};
</
script
>
<
template
>
<gl-filtered-search-token
:config=
"config"
v-bind=
"
{ ...$props, ...$attrs }"
v-on="$listeners"
@input="updateWeights"
>
<template
#suggestions
>
<gl-filtered-search-suggestion
v-for=
"weight in defaultWeights"
:key=
"weight.value"
:value=
"weight.value"
>
{{
weight
.
text
}}
</gl-filtered-search-suggestion>
<gl-dropdown-divider
v-if=
"defaultWeights.length"
/>
<gl-filtered-search-suggestion
v-for=
"weight of weights"
:key=
"weight"
:value=
"weight"
>
{{
weight
}}
</gl-filtered-search-suggestion>
</
template
>
</gl-filtered-search-token>
</template>
ee/app/helpers/ee/issues_helper.rb
View file @
2795812e
...
@@ -71,11 +71,17 @@ module EE
...
@@ -71,11 +71,17 @@ module EE
override
:issues_list_data
override
:issues_list_data
def
issues_list_data
(
project
,
current_user
,
finder
)
def
issues_list_data
(
project
,
current_user
,
finder
)
super
.
merge!
(
data
=
super
.
merge!
(
has_blocked_issues_feature:
project
.
feature_available?
(
:blocked_issues
).
to_s
,
has_blocked_issues_feature:
project
.
feature_available?
(
:blocked_issues
).
to_s
,
has_issuable_health_status_feature:
project
.
feature_available?
(
:issuable_health_status
).
to_s
,
has_issuable_health_status_feature:
project
.
feature_available?
(
:issuable_health_status
).
to_s
,
has_issue_weights_feature:
project
.
feature_available?
(
:issue_weights
).
to_s
has_issue_weights_feature:
project
.
feature_available?
(
:issue_weights
).
to_s
)
)
if
project
.
feature_available?
(
:iterations
)
data
[
:project_iterations_path
]
=
api_v4_projects_iterations_path
(
id:
project
.
id
)
end
data
end
end
end
end
end
end
ee/spec/helpers/ee/issues_helper_spec.rb
View file @
2795812e
...
@@ -125,23 +125,49 @@ RSpec.describe EE::IssuesHelper do
...
@@ -125,23 +125,49 @@ RSpec.describe EE::IssuesHelper do
end
end
describe
'#issues_list_data'
do
describe
'#issues_list_data'
do
it
'returns expected result'
do
let
(
:current_user
)
{
double
.
as_null_object
}
current_user
=
double
.
as_null_object
let
(
:finder
)
{
double
.
as_null_object
}
finder
=
double
.
as_null_object
before
do
allow
(
helper
).
to
receive
(
:current_user
).
and_return
(
current_user
)
allow
(
helper
).
to
receive
(
:current_user
).
and_return
(
current_user
)
allow
(
helper
).
to
receive
(
:finder
).
and_return
(
finder
)
allow
(
helper
).
to
receive
(
:can?
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:url_for
).
and_return
(
'#'
)
allow
(
helper
).
to
receive
(
:url_for
).
and_return
(
'#'
)
allow
(
helper
).
to
receive
(
:import_csv_namespace_project_issues_path
).
and_return
(
'#'
)
allow
(
helper
).
to
receive
(
:import_csv_namespace_project_issues_path
).
and_return
(
'#'
)
allow
(
project
).
to
receive
(
:feature_available?
).
and_return
(
true
)
end
expected
=
{
context
'when features are enabled'
do
has_blocked_issues_feature:
'true'
,
before
do
has_issuable_health_status_feature:
'true'
,
stub_licensed_features
(
iterations:
true
,
issue_weights:
true
,
issuable_health_status:
true
,
blocked_issues:
true
)
has_issue_weights_feature:
'true'
end
}
expect
(
helper
.
issues_list_data
(
project
,
current_user
,
finder
)).
to
include
(
expected
)
it
'returns data with licensed features enabled'
do
expected
=
{
has_blocked_issues_feature:
'true'
,
has_issuable_health_status_feature:
'true'
,
has_issue_weights_feature:
'true'
,
project_iterations_path:
api_v4_projects_iterations_path
(
id:
project
.
id
)
}
expect
(
helper
.
issues_list_data
(
project
,
current_user
,
finder
)).
to
include
(
expected
)
end
end
context
'when features are disabled'
do
before
do
stub_licensed_features
(
iterations:
false
,
issue_weights:
false
,
issuable_health_status:
false
,
blocked_issues:
false
)
end
it
'returns data with licensed features disabled'
do
expected
=
{
has_blocked_issues_feature:
'false'
,
has_issuable_health_status_feature:
'false'
,
has_issue_weights_feature:
'false'
}
result
=
helper
.
issues_list_data
(
project
,
current_user
,
finder
)
expect
(
result
).
to
include
(
expected
)
expect
(
result
).
not_to
include
(
:project_iterations_path
)
end
end
end
end
end
end
end
locale/gitlab.pot
View file @
2795812e
...
@@ -32177,6 +32177,9 @@ msgstr ""
...
@@ -32177,6 +32177,9 @@ msgstr ""
msgid "There was a problem fetching groups."
msgid "There was a problem fetching groups."
msgstr ""
msgstr ""
msgid "There was a problem fetching iterations."
msgstr ""
msgid "There was a problem fetching labels."
msgid "There was a problem fetching labels."
msgstr ""
msgstr ""
...
...
spec/frontend/issues_list/mock_data.js
View file @
2795812e
...
@@ -14,6 +14,10 @@ export const locationSearch = [
...
@@ -14,6 +14,10 @@ export const locationSearch = [
'
not[label_name][]=drama
'
,
'
not[label_name][]=drama
'
,
'
my_reaction_emoji=thumbsup
'
,
'
my_reaction_emoji=thumbsup
'
,
'
confidential=no
'
,
'
confidential=no
'
,
'
iteration_title=season:+%234
'
,
'
not[iteration_title]=season:+%2320
'
,
'
weight=1
'
,
'
not[weight]=3
'
,
].
join
(
'
&
'
);
].
join
(
'
&
'
);
export
const
filteredTokens
=
[
export
const
filteredTokens
=
[
...
@@ -29,6 +33,10 @@ export const filteredTokens = [
...
@@ -29,6 +33,10 @@ export const filteredTokens = [
{
type
:
'
labels
'
,
value
:
{
data
:
'
drama
'
,
operator
:
OPERATOR_IS_NOT
}
},
{
type
:
'
labels
'
,
value
:
{
data
:
'
drama
'
,
operator
:
OPERATOR_IS_NOT
}
},
{
type
:
'
my_reaction_emoji
'
,
value
:
{
data
:
'
thumbsup
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
my_reaction_emoji
'
,
value
:
{
data
:
'
thumbsup
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
confidential
'
,
value
:
{
data
:
'
no
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
confidential
'
,
value
:
{
data
:
'
no
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
iteration
'
,
value
:
{
data
:
'
season: #4
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
iteration
'
,
value
:
{
data
:
'
season: #20
'
,
operator
:
OPERATOR_IS_NOT
}
},
{
type
:
'
weight
'
,
value
:
{
data
:
'
1
'
,
operator
:
OPERATOR_IS
}
},
{
type
:
'
weight
'
,
value
:
{
data
:
'
3
'
,
operator
:
OPERATOR_IS_NOT
}
},
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
find
'
}
},
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
find
'
}
},
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
issues
'
}
},
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
issues
'
}
},
];
];
...
@@ -44,6 +52,10 @@ export const apiParams = {
...
@@ -44,6 +52,10 @@ export const apiParams = {
'
not[labels]
'
:
'
live action,drama
'
,
'
not[labels]
'
:
'
live action,drama
'
,
my_reaction_emoji
:
'
thumbsup
'
,
my_reaction_emoji
:
'
thumbsup
'
,
confidential
:
'
no
'
,
confidential
:
'
no
'
,
iteration_title
:
'
season: #4
'
,
'
not[iteration_title]
'
:
'
season: #20
'
,
weight
:
'
1
'
,
'
not[weight]
'
:
'
3
'
,
};
};
export
const
urlParams
=
{
export
const
urlParams
=
{
...
@@ -57,4 +69,8 @@ export const urlParams = {
...
@@ -57,4 +69,8 @@ export const urlParams = {
'
not[label_name][]
'
:
[
'
live action
'
,
'
drama
'
],
'
not[label_name][]
'
:
[
'
live action
'
,
'
drama
'
],
my_reaction_emoji
:
[
'
thumbsup
'
],
my_reaction_emoji
:
[
'
thumbsup
'
],
confidential
:
[
'
no
'
],
confidential
:
[
'
no
'
],
iteration_title
:
[
'
season: #4
'
],
'
not[iteration_title]
'
:
[
'
season: #20
'
],
weight
:
[
'
1
'
],
'
not[weight]
'
:
[
'
3
'
],
};
};
spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
View file @
2795812e
...
@@ -5,8 +5,10 @@ import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/auth
...
@@ -5,8 +5,10 @@ import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/auth
import
BranchToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue
'
;
import
BranchToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue
'
;
import
EmojiToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
'
;
import
EmojiToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
'
;
import
EpicToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue
'
;
import
EpicToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue
'
;
import
IterationToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue
'
;
import
LabelToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/label_token.vue
'
;
import
LabelToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/label_token.vue
'
;
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
WeightToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
'
;
export
const
mockAuthor1
=
{
export
const
mockAuthor1
=
{
id
:
1
,
id
:
1
,
...
@@ -98,6 +100,15 @@ export const mockAuthorToken = {
...
@@ -98,6 +100,15 @@ export const mockAuthorToken = {
fetchAuthors
:
Api
.
projectUsers
.
bind
(
Api
),
fetchAuthors
:
Api
.
projectUsers
.
bind
(
Api
),
};
};
export
const
mockIterationToken
=
{
type
:
'
iteration
'
,
icon
:
'
iteration
'
,
title
:
'
Iteration
'
,
unique
:
true
,
token
:
IterationToken
,
fetchIterations
:
()
=>
Promise
.
resolve
(),
};
export
const
mockLabelToken
=
{
export
const
mockLabelToken
=
{
type
:
'
label_name
'
,
type
:
'
label_name
'
,
icon
:
'
labels
'
,
icon
:
'
labels
'
,
...
@@ -155,6 +166,14 @@ export const mockMembershipToken = {
...
@@ -155,6 +166,14 @@ export const mockMembershipToken = {
],
],
};
};
export
const
mockWeightToken
=
{
type
:
'
weight
'
,
icon
:
'
weight
'
,
title
:
'
Weight
'
,
unique
:
true
,
token
:
WeightToken
,
};
export
const
mockMembershipTokenOptionsWithoutTitles
=
{
export
const
mockMembershipTokenOptionsWithoutTitles
=
{
...
mockMembershipToken
,
...
mockMembershipToken
,
options
:
[{
value
:
'
exclude
'
},
{
value
:
'
only
'
}],
options
:
[{
value
:
'
exclude
'
},
{
value
:
'
only
'
}],
...
...
spec/frontend/vue_shared/components/filtered_search_bar/tokens/iteration_token_spec.js
0 → 100644
View file @
2795812e
import
{
GlFilteredSearchToken
,
GlFilteredSearchTokenSegment
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
createFlash
from
'
~/flash
'
;
import
IterationToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue
'
;
import
{
mockIterationToken
}
from
'
../mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
describe
(
'
IterationToken
'
,
()
=>
{
const
title
=
'
gitlab-org: #1
'
;
let
wrapper
;
const
createComponent
=
({
config
=
mockIterationToken
,
value
=
{
data
:
''
}
}
=
{})
=>
mount
(
IterationToken
,
{
propsData
:
{
config
,
value
,
},
provide
:
{
portalName
:
'
fake target
'
,
alignSuggestions
:
function
fakeAlignSuggestions
()
{},
suggestionsListClass
:
'
custom-class
'
,
},
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
renders iteration value
'
,
async
()
=>
{
wrapper
=
createComponent
({
value
:
{
data
:
title
}
});
await
wrapper
.
vm
.
$nextTick
();
const
tokenSegments
=
wrapper
.
findAllComponents
(
GlFilteredSearchTokenSegment
);
expect
(
tokenSegments
).
toHaveLength
(
3
);
// `Iteration` `=` `gitlab-org: #1`
expect
(
tokenSegments
.
at
(
2
).
text
()).
toBe
(
title
);
});
it
(
'
fetches initial values
'
,
()
=>
{
const
fetchIterationsSpy
=
jest
.
fn
().
mockResolvedValue
();
wrapper
=
createComponent
({
config
:
{
...
mockIterationToken
,
fetchIterations
:
fetchIterationsSpy
},
value
:
{
data
:
title
},
});
expect
(
fetchIterationsSpy
).
toHaveBeenCalledWith
(
title
);
});
it
(
'
fetches iterations on user input
'
,
()
=>
{
const
search
=
'
hello
'
;
const
fetchIterationsSpy
=
jest
.
fn
().
mockResolvedValue
();
wrapper
=
createComponent
({
config
:
{
...
mockIterationToken
,
fetchIterations
:
fetchIterationsSpy
},
});
wrapper
.
findComponent
(
GlFilteredSearchToken
).
vm
.
$emit
(
'
input
'
,
{
data
:
search
});
expect
(
fetchIterationsSpy
).
toHaveBeenCalledWith
(
search
);
});
it
(
'
renders error message when request fails
'
,
async
()
=>
{
const
fetchIterationsSpy
=
jest
.
fn
().
mockRejectedValue
();
wrapper
=
createComponent
({
config
:
{
...
mockIterationToken
,
fetchIterations
:
fetchIterationsSpy
},
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
createFlash
).
toHaveBeenCalledWith
({
message
:
'
There was a problem fetching iterations.
'
,
});
});
});
spec/frontend/vue_shared/components/filtered_search_bar/tokens/weight_token_spec.js
0 → 100644
View file @
2795812e
import
{
GlFilteredSearchTokenSegment
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
WeightToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
'
;
import
{
mockWeightToken
}
from
'
../mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
describe
(
'
WeightToken
'
,
()
=>
{
const
weight
=
'
3
'
;
let
wrapper
;
const
createComponent
=
({
config
=
mockWeightToken
,
value
=
{
data
:
''
}
}
=
{})
=>
mount
(
WeightToken
,
{
propsData
:
{
config
,
value
,
},
provide
:
{
portalName
:
'
fake target
'
,
alignSuggestions
:
function
fakeAlignSuggestions
()
{},
suggestionsListClass
:
'
custom-class
'
,
},
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
renders weight value
'
,
()
=>
{
wrapper
=
createComponent
({
value
:
{
data
:
weight
}
});
const
tokenSegments
=
wrapper
.
findAllComponents
(
GlFilteredSearchTokenSegment
);
expect
(
tokenSegments
).
toHaveLength
(
3
);
// `Weight` `=` `3`
expect
(
tokenSegments
.
at
(
2
).
text
()).
toBe
(
weight
);
});
});
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