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
2ba8c796
Commit
2ba8c796
authored
Nov 10, 2020
by
Daniel Tian
Committed by
Jose Ivan Vargas
Nov 10, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve vulnerability filter component logic
parent
6b18dd94
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
442 additions
and
363 deletions
+442
-363
ee/app/assets/javascripts/security_dashboard/components/filters/filter_body.vue
...pts/security_dashboard/components/filters/filter_body.vue
+1
-1
ee/app/assets/javascripts/security_dashboard/components/filters/standard_filter.vue
...security_dashboard/components/filters/standard_filter.vue
+76
-14
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
...ashboard/components/first_class_vulnerability_filters.vue
+24
-67
ee/app/assets/javascripts/security_dashboard/helpers.js
ee/app/assets/javascripts/security_dashboard/helpers.js
+34
-34
ee/app/assets/javascripts/security_dashboard/store/modules/filters/constants.js
...pts/security_dashboard/store/modules/filters/constants.js
+4
-0
ee/spec/frontend/security_dashboard/components/filters/filter_body_spec.js
...security_dashboard/components/filters/filter_body_spec.js
+6
-4
ee/spec/frontend/security_dashboard/components/filters/standard_filter_spec.js
...rity_dashboard/components/filters/standard_filter_spec.js
+277
-67
ee/spec/frontend/security_dashboard/components/filters_spec.js
...ec/frontend/security_dashboard/components/filters_spec.js
+2
-2
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
...oard/components/first_class_vulnerability_filters_spec.js
+18
-174
No files found.
ee/app/assets/javascripts/security_dashboard/components/filters/filter_body.vue
View file @
2ba8c796
...
...
@@ -31,7 +31,7 @@ export default {
},
computed
:
{
firstSelectedOption
()
{
return
this
.
selectedOptions
[
0
]
||
'
-
'
;
return
this
.
selectedOptions
[
0
]
?.
name
||
'
-
'
;
},
extraOptionCount
()
{
return
this
.
selectedOptions
.
length
-
1
;
...
...
ee/app/assets/javascripts/security_dashboard/components/filters/standard_filter.vue
View file @
2ba8c796
<
script
>
import
{
isEqual
,
xor
}
from
'
lodash
'
;
import
FilterBody
from
'
./filter_body.vue
'
;
import
FilterItem
from
'
./filter_item.vue
'
;
export
default
{
components
:
{
FilterBody
,
FilterItem
,
},
components
:
{
FilterBody
,
FilterItem
},
props
:
{
filter
:
{
type
:
Object
,
required
:
true
,
},
showSearchBox
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
searchTerm
:
''
,
selectedOptions
:
undefined
,
};
},
computed
:
{
selection
()
{
return
this
.
filter
.
selection
;
selectedSet
()
{
return
new
Set
(
this
.
selectedOptions
);
},
isNoOptionsSelected
()
{
return
this
.
selectedOptions
.
length
<=
0
;
},
selectedOptionsOrAll
()
{
return
this
.
selectedOptions
.
length
?
this
.
selectedOptions
:
[
this
.
filter
.
allOption
];
},
queryObject
()
{
// This is the object used to update the querystring.
return
{
[
this
.
filter
.
id
]:
this
.
selectedOptionsOrAll
.
map
(
x
=>
x
.
id
)
};
},
filterObject
()
{
// This is the object used by the GraphQL query.
return
{
[
this
.
filter
.
id
]:
this
.
selectedOptions
.
map
(
x
=>
x
.
id
)
};
},
filteredOptions
()
{
return
this
.
filter
.
options
.
filter
(
option
=>
option
.
name
.
toLowerCase
().
includes
(
this
.
searchTerm
.
toLowerCase
()),
);
},
selectedOptionsNames
()
{
return
Array
.
from
(
this
.
selection
).
map
(
id
=>
this
.
filter
.
options
.
find
(
x
=>
x
.
id
===
id
).
name
);
routeQueryIds
()
{
const
ids
=
this
.
$route
.
query
[
this
.
filter
.
id
]
||
[];
return
Array
.
isArray
(
ids
)
?
ids
:
[
ids
];
},
routeQueryOptions
()
{
const
options
=
this
.
filter
.
options
.
filter
(
x
=>
this
.
routeQueryIds
.
includes
(
x
.
id
));
const
hasAllId
=
this
.
routeQueryIds
.
includes
(
this
.
filter
.
allOption
.
id
);
if
(
options
.
length
&&
!
hasAllId
)
{
return
options
;
}
return
hasAllId
?
[]
:
this
.
filter
.
defaultOptions
;
},
},
watch
:
{
selectedOptions
()
{
this
.
$emit
(
'
filter-changed
'
,
this
.
filterObject
);
},
},
created
()
{
this
.
selectedOptions
=
this
.
routeQueryOptions
;
// When the user clicks the forward/back browser buttons, update the selected options.
window
.
addEventListener
(
'
popstate
'
,
()
=>
{
this
.
selectedOptions
=
this
.
routeQueryOptions
;
});
},
methods
:
{
clickFilter
(
option
)
{
this
.
$emit
(
'
setFilter
'
,
{
filterId
:
this
.
filter
.
id
,
optionId
:
option
.
id
});
toggleOption
(
option
)
{
// Toggle the option's existence in the array.
this
.
selectedOptions
=
xor
(
this
.
selectedOptions
,
[
option
]);
this
.
updateRouteQuery
();
},
deselectAllOptions
()
{
this
.
selectedOptions
=
[];
this
.
updateRouteQuery
();
},
updateRouteQuery
()
{
const
query
=
{
query
:
{
...
this
.
$route
.
query
,
...
this
.
queryObject
}
};
// To avoid a console error, don't update the querystring if it's the same as the current one.
if
(
!
isEqual
(
this
.
routeQueryIds
,
this
.
queryObject
[
this
.
filter
.
id
]))
{
this
.
$router
.
push
(
query
);
}
},
isSelected
(
option
)
{
return
this
.
select
ion
.
has
(
option
.
id
);
return
this
.
select
edSet
.
has
(
option
);
},
},
};
...
...
@@ -46,15 +100,23 @@ export default {
<filter-body
v-model.trim=
"searchTerm"
:name=
"filter.name"
:selected-options=
"selectedOptions
Names
"
:show-search-box=
"
filter.options.length >= 20
"
:selected-options=
"selectedOptions
OrAll
"
:show-search-box=
"
showSearchBox
"
>
<filter-item
v-if=
"filter.allOption && !searchTerm.length"
:is-checked=
"isNoOptionsSelected"
:text=
"filter.allOption.name"
data-testid=
"allOption"
@
click=
"deselectAllOptions"
/>
<filter-item
v-for=
"option in filteredOptions"
:key=
"option.id"
:is-checked=
"isSelected(option)"
:text=
"option.name"
@
click=
"clickFilter(option)"
data-testid=
"filterOption"
@
click=
"toggleOption(option)"
/>
</filter-body>
</
template
>
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
View file @
2ba8c796
<
script
>
import
{
isEqual
}
from
'
lodash
'
;
import
{
ALL
,
STATE
}
from
'
ee/security_dashboard/store/modules/filters/constant
s
'
;
import
{
setFilter
}
from
'
ee/security_dashboard/store/modules/filters/utils
'
;
import
StandardFilter
from
'
ee/security_dashboard/components/filters/standard_filter.vue
'
;
import
{
initFirstClassVulnerabilityFilters
,
mapProjects
}
from
'
ee/security_dashboard/helpers
'
;
import
{
debounce
}
from
'
lodash
'
;
import
{
stateFilter
,
severityFilter
,
scannerFilter
,
getProjectFilter
}
from
'
../helper
s
'
;
import
StandardFilter
from
'
./filters/standard_filter.vue
'
;
const
searchBoxOptionCount
=
20
;
// Number of options before the search box is shown.
export
default
{
components
:
{
...
...
@@ -12,73 +12,28 @@ export default {
props
:
{
projects
:
{
type
:
Array
,
required
:
false
,
default
:
undefined
},
},
data
()
{
return
{
filters
:
initFirstClassVulnerabilityFilters
(
this
.
projects
),
};
},
data
:
()
=>
({
filterQuery
:
{},
}),
computed
:
{
selectedFilters
()
{
return
this
.
filters
.
reduce
((
acc
,
{
id
,
selection
})
=>
{
if
(
!
selection
.
has
(
ALL
))
{
acc
[
id
]
=
Array
.
from
(
selection
);
}
return
acc
;
},
{});
},
},
watch
:
{
/**
* Initially the project list empty. We fetch them dynamically from GraphQL while
* fetching the list of vulnerabilities. We display the project filter with the base
* option and when the projects are fetched we add them to the list.
*/
projects
(
newProjects
,
oldProjects
)
{
if
(
oldProjects
.
length
===
0
)
{
const
projectFilter
=
this
.
filters
[
3
];
projectFilter
.
options
=
[
projectFilter
.
options
[
0
],
...
mapProjects
(
this
.
projects
)];
}
},
'
$route.query
'
:
{
immediate
:
true
,
handler
(
newQuery
)
{
let
changed
;
this
.
filters
.
forEach
((
filter
,
i
)
=>
{
let
urlFilter
=
newQuery
[
filter
.
id
];
if
(
typeof
urlFilter
===
'
undefined
'
)
{
urlFilter
=
[
ALL
];
}
else
if
(
!
Array
.
isArray
(
urlFilter
))
{
urlFilter
=
[
urlFilter
];
}
if
(
isEqual
(
this
.
selectedFilters
[
filter
.
id
],
newQuery
[
filter
.
id
])
===
false
)
{
changed
=
true
;
this
.
filters
[
i
].
selection
=
new
Set
(
urlFilter
);
}
});
if
(
changed
)
{
this
.
$emit
(
'
filterChange
'
,
this
.
selectedFilters
);
}
},
filters
()
{
return
this
.
projects
?
[
stateFilter
,
severityFilter
,
scannerFilter
,
getProjectFilter
(
this
.
projects
)]
:
[
stateFilter
,
severityFilter
,
scannerFilter
];
},
},
created
()
{
if
(
Object
.
keys
(
this
.
selectedFilters
).
length
===
0
)
{
this
.
$router
.
push
({
query
:
{
state
:
[
STATE
.
DETECTED
,
STATE
.
CONFIRMED
]
}
});
}
},
methods
:
{
setFilter
(
options
)
{
this
.
filters
=
setFilter
(
this
.
filters
,
options
);
this
.
$router
.
push
({
query
:
this
.
selectedFilters
});
this
.
$emit
(
'
filterChange
'
,
this
.
selectedFilters
);
updateFilterQuery
(
query
)
{
this
.
filterQuery
=
{
...
this
.
filterQuery
,
...
query
};
this
.
emitFilterChange
();
},
// All the filters will emit @filter-changed when the page is first loaded, which will trigger
// this method multiple times. We'll debounce it so that it only runs once.
emitFilterChange
:
debounce
(
function
emit
()
{
this
.
$emit
(
'
filterChange
'
,
this
.
filterQuery
);
}),
},
searchBoxOptionCount
,
};
</
script
>
...
...
@@ -90,7 +45,9 @@ export default {
:key=
"filter.id"
class=
"col-sm-6 col-md-4 col-lg-2 p-2"
:filter=
"filter"
@
setFilter=
"setFilter"
:data-testid=
"filter.id"
:show-search-box=
"filter.options.length >= $options.searchBoxOptionCount"
@
filter-changed=
"updateFilterQuery"
/>
</div>
</div>
...
...
ee/app/assets/javascripts/security_dashboard/helpers.js
View file @
2ba8c796
import
isPlainObject
from
'
lodash/isPlainObject
'
;
import
{
ALL
,
BASE_FILTERS
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
BASE_FILTERS
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
REPORT_TYPES
,
SEVERITY_LEVELS
}
from
'
ee/security_dashboard/store/constants
'
;
import
{
VULNERABILITY_STATES
}
from
'
ee/vulnerabilities/constants
'
;
import
{
convertObjectPropsToSnakeCase
}
from
'
~/lib/utils/common_utils
'
;
...
...
@@ -11,41 +11,41 @@ const parseOptions = obj =>
export
const
mapProjects
=
projects
=>
projects
.
map
(
p
=>
({
id
:
p
.
id
.
split
(
'
/
'
).
pop
(),
name
:
p
.
name
}));
export
const
initFirstClassVulnerabilityFilters
=
projects
=>
{
const
filters
=
[
{
name
:
s__
(
'
SecurityReports|Status
'
),
id
:
'
state
'
,
options
:
[
{
id
:
ALL
,
name
:
s__
(
'
VulnerabilityStatusTypes|All
'
)
},
...
parseOptions
(
VULNERABILITY_STATES
),
],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
SecurityReports|Severity
'
),
id
:
'
severity
'
,
options
:
[
BASE_FILTERS
.
severity
,
...
parseOptions
(
SEVERITY_LEVELS
)],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
Reports|Scanner
'
),
id
:
'
reportType
'
,
options
:
[
BASE_FILTERS
.
report_type
,
...
parseOptions
(
REPORT_TYPES
)],
selection
:
new
Set
([
ALL
]),
},
];
const
stateOptions
=
parseOptions
(
VULNERABILITY_STATES
);
const
defaultStateOptions
=
stateOptions
.
filter
(
x
=>
[
'
DETECTED
'
,
'
CONFIRMED
'
].
includes
(
x
.
id
));
if
(
Array
.
isArray
(
projects
))
{
filters
.
push
({
name
:
s__
(
'
SecurityReports|Project
'
),
id
:
'
projectId
'
,
options
:
[
BASE_FILTERS
.
project_id
,
...
mapProjects
(
projects
)],
selection
:
new
Set
([
ALL
]),
});
}
export
const
stateFilter
=
{
name
:
s__
(
'
SecurityReports|Status
'
),
id
:
'
state
'
,
options
:
stateOptions
,
allOption
:
BASE_FILTERS
.
state
,
defaultOptions
:
defaultStateOptions
,
};
export
const
severityFilter
=
{
name
:
s__
(
'
SecurityReports|Severity
'
),
id
:
'
severity
'
,
options
:
parseOptions
(
SEVERITY_LEVELS
),
allOption
:
BASE_FILTERS
.
severity
,
defaultOptions
:
[],
};
export
const
scannerFilter
=
{
name
:
s__
(
'
Reports|Scanner
'
),
id
:
'
reportType
'
,
options
:
parseOptions
(
REPORT_TYPES
),
allOption
:
BASE_FILTERS
.
report_type
,
defaultOptions
:
[],
};
return
filters
;
export
const
getProjectFilter
=
projects
=>
{
return
{
name
:
s__
(
'
SecurityReports|Project
'
),
id
:
'
projectId
'
,
options
:
mapProjects
(
projects
),
allOption
:
BASE_FILTERS
.
project_id
,
defaultOptions
:
[],
};
};
/**
...
...
ee/app/assets/javascripts/security_dashboard/store/modules/filters/constants.js
View file @
2ba8c796
...
...
@@ -7,6 +7,10 @@ export const STATE = {
};
export
const
BASE_FILTERS
=
{
state
:
{
name
:
s__
(
'
VulnerabilityStatusTypes|All
'
),
id
:
ALL
,
},
severity
:
{
name
:
s__
(
'
ciReport|All severities
'
),
id
:
ALL
,
...
...
ee/spec/frontend/security_dashboard/components/filters/filter_body_spec.js
View file @
2ba8c796
...
...
@@ -33,17 +33,19 @@ describe('Filter Body component', () => {
describe
(
'
dropdown button
'
,
()
=>
{
it
(
'
shows the selected option name if only one option is selected
'
,
()
=>
{
const
props
=
{
selectedOptions
:
[
'
Some Selected Option
'
]
};
const
option
=
{
name
:
'
Some Selected Option
'
};
const
props
=
{
selectedOptions
:
[
option
]
};
createComponent
(
props
);
expect
(
dropdownButton
().
text
()).
toBe
(
props
.
selectedOptions
[
0
]
);
expect
(
dropdownButton
().
text
()).
toBe
(
option
.
name
);
});
it
(
'
shows the selected option name and "+x more" if more than one option is selected
'
,
()
=>
{
const
props
=
{
selectedOptions
:
[
'
Option 1
'
,
'
Option 2
'
,
'
Option 3
'
]
};
const
options
=
[{
name
:
'
Option 1
'
},
{
name
:
'
Option 2
'
},
{
name
:
'
Option 3
'
}];
const
props
=
{
selectedOptions
:
options
};
createComponent
(
props
);
expect
(
dropdownButton
().
text
()).
toMatch
(
/Option 1
\s
+
\+
2 more/
);
expect
(
dropdownButton
().
text
()).
toMatch
InterpolatedText
(
'
Option 1 +2 more
'
);
});
});
...
...
ee/spec/frontend/security_dashboard/components/filters/standard_filter_spec.js
View file @
2ba8c796
This diff is collapsed.
Click to expand it.
ee/spec/frontend/security_dashboard/components/filters_spec.js
View file @
2ba8c796
import
Vuex
from
'
vuex
'
;
import
Filters
from
'
ee/security_dashboard/components/filters.vue
'
;
import
createStore
from
'
ee/security_dashboard/store
'
;
import
{
m
ount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
shallowM
ount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
...
...
@@ -11,7 +11,7 @@ describe('Filter component', () => {
let
store
;
const
createWrapper
=
(
props
=
{})
=>
{
wrapper
=
m
ount
(
Filters
,
{
wrapper
=
shallowM
ount
(
Filters
,
{
localVue
,
store
,
propsData
:
{
...
...
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
View file @
2ba8c796
import
VueRouter
from
'
vue-router
'
;
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
initFirstClassVulnerabilityFilters
}
from
'
ee/security_dashboard/helpers
'
;
import
Filters
from
'
ee/security_dashboard/components/first_class_vulnerability_filters.vue
'
;
import
StandardFilter
from
'
ee/security_dashboard/components/filters/standard_filter.vue
'
;
...
...
@@ -10,7 +9,6 @@ localVue.use(VueRouter);
describe
(
'
First class vulnerability filters component
'
,
()
=>
{
let
wrapper
;
let
filters
;
const
projects
=
[
{
id
:
'
gid://gitlab/Project/11
'
,
name
:
'
GitLab Org
'
},
...
...
@@ -18,11 +16,8 @@ describe('First class vulnerability filters component', () => {
];
const
findFilters
=
()
=>
wrapper
.
findAll
(
StandardFilter
);
const
findStateFilter
=
()
=>
findFilters
().
at
(
0
);
const
findSeverityFilter
=
()
=>
findFilters
().
at
(
1
);
const
findReportTypeFilter
=
()
=>
findFilters
().
at
(
2
);
const
findProjectFilter
=
()
=>
findFilters
().
at
(
3
);
const
findLastFilter
=
()
=>
findFilters
().
at
(
filters
.
length
-
1
);
const
findStateFilter
=
()
=>
wrapper
.
find
(
'
[data-testid="state"]
'
);
const
findProjectFilter
=
()
=>
wrapper
.
find
(
'
[data-testid="projectId"]
'
);
const
createComponent
=
({
propsData
,
listeners
}
=
{})
=>
{
return
shallowMount
(
Filters
,
{
localVue
,
router
,
propsData
,
listeners
});
...
...
@@ -36,190 +31,39 @@ describe('First class vulnerability filters component', () => {
describe
(
'
on render without project filter
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
();
filters
=
initFirstClassVulnerabilityFilters
();
});
it
(
'
should render the filters
'
,
()
=>
{
expect
(
findFilters
()).
toHaveLength
(
filters
.
length
);
it
(
'
should render the
default
filters
'
,
()
=>
{
expect
(
findFilters
()).
toHaveLength
(
3
);
});
it
(
'
should call the setFilter mutation when setting a filter
'
,
()
=>
{
const
stub
=
jest
.
fn
();
it
(
'
should emit filterChange when a filter is changed
'
,
()
=>
{
const
options
=
{
foo
:
'
bar
'
};
findStateFilter
().
vm
.
$emit
(
'
filter-changed
'
,
options
);
wrapper
.
setMethods
({
setFilter
:
stub
});
findStateFilter
().
vm
.
$emit
(
'
setFilter
'
,
options
);
expect
(
stub
).
toHaveBeenCalledWith
(
options
);
expect
(
wrapper
.
emitted
(
'
filterChange
'
)[
0
][
0
]).
toEqual
(
options
);
});
});
describe
(
'
when project filter is populated dynamically
'
,
()
=>
{
beforeEach
(()
=>
{
filters
=
initFirstClassVulnerabilityFilters
([]);
wrapper
=
createComponent
({
propsData
:
{
projects
:
[]
}
});
wrapper
=
createComponent
();
});
it
(
'
should render the project filter with one option
'
,
()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
({
id
:
'
projectId
'
,
name
:
'
Project
'
,
options
:
[{
id
:
'
all
'
,
name
:
'
All projects
'
}],
selection
:
new
Set
([
'
all
'
]),
});
it
(
'
should render the project filter with no options
'
,
async
()
=>
{
wrapper
.
setProps
({
projects
:
[]
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findProjectFilter
().
props
(
'
filter
'
).
options
).
toHaveLength
(
0
);
});
it
(
'
should
set the projects dynamically
'
,
()
=>
{
it
(
'
should
render the project filter with the expected options
'
,
async
()
=>
{
wrapper
.
setProps
({
projects
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
(
expect
.
objectContaining
({
options
:
[
{
id
:
'
all
'
,
name
:
'
All projects
'
},
{
id
:
'
11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
12
'
,
name
:
'
GitLab Com
'
},
],
}),
);
});
});
});
describe
(
'
when project filter is ready on mount
'
,
()
=>
{
beforeEach
(()
=>
{
filters
=
initFirstClassVulnerabilityFilters
([]);
wrapper
=
createComponent
({
propsData
:
{
projects
}
});
});
it
(
'
should set the projects dynamically
'
,
()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
(
expect
.
objectContaining
({
options
:
[
{
id
:
'
all
'
,
name
:
'
All projects
'
},
{
id
:
'
11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
12
'
,
name
:
'
GitLab Com
'
},
],
}),
);
});
});
describe
(
'
when no filter is persisted in the URL
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
propsData
:
{
projects
},
});
});
it
(
'
should redirect the user to an updated the URL and default the filters to CONFIRMED + DETECTED state
'
,
()
=>
{
expect
(
findStateFilter
().
props
(
'
filter
'
)).
toEqual
(
expect
.
objectContaining
({
selection
:
new
Set
([
'
DETECTED
'
,
'
CONFIRMED
'
]),
}),
);
});
});
describe
.
each
`
filter | value | selector
${
'
state
'
}
|
${
'
DETECTED,DISMISSED
'
}
|
${
findStateFilter
}
${
'
severity
'
}
|
${
'
MEDIUM
'
}
|
${
findSeverityFilter
}
${
'
reportType
'
}
|
${
'
SAST
'
}
|
${
findReportTypeFilter
}
${
'
projectId
'
}
|
${
'
12
'
}
|
${
findProjectFilter
}
`
(
'
when filters are persisted
'
,
({
filter
,
value
,
selector
})
=>
{
describe
(
`with filter set to
${
filter
}
:
${
value
}
`
,
()
=>
{
let
filterChangeSpy
;
beforeEach
(()
=>
{
filterChangeSpy
=
jest
.
fn
();
wrapper
=
createComponent
({
propsData
:
{
projects
},
listeners
:
{
filterChange
:
filterChangeSpy
},
});
// reset the router query in-between test cases
router
.
push
({
query
:
{}
});
router
.
push
({
query
:
{
[
filter
]:
value
.
split
(
'
,
'
)
}
},
()
=>
{});
});
it
(
`should have the
${
filter
}
filter as pre-selected`
,
()
=>
{
expect
(
selector
().
props
(
'
filter
'
).
selection
).
toEqual
(
new
Set
(
value
.
split
(
'
,
'
)));
});
it
(
'
should emit a filterChange event
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
});
it
(
'
should not trigger the filterChange additonally when the filters do not change
'
,
()
=>
{
router
.
push
({
query
:
{
...
wrapper
.
vm
.
$route
.
query
,
'
some-unrelated-query-param
'
:
'
true
'
,
},
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
filterChangeSpy
).
toHaveBeenCalledTimes
(
1
);
});
});
it
(
'
should trigger the filterChange when the filters are reset
'
,
()
=>
{
router
.
push
({
query
:
{}
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
filterChangeSpy
).
toHaveBeenNthCalledWith
(
2
,
{});
});
});
it
(
'
should reset the filters when the URL contains no more filters
'
,
()
=>
{
router
.
push
({
query
:
{}
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
selector
().
props
(
'
filter
'
).
selection
).
toEqual
(
new
Set
([
'
all
'
]));
});
});
});
});
describe
.
each
`
filter | selector | index
${
'
state
'
}
|
${
findStateFilter
}
|
${
0
}
${
'
severity
'
}
|
${
findSeverityFilter
}
|
${
1
}
${
'
reportType
'
}
|
${
findReportTypeFilter
}
|
${
2
}
${
'
projectId
'
}
|
${
findProjectFilter
}
|
${
3
}
`
(
'
when setFilter is called
'
,
({
filter
,
selector
,
index
})
=>
{
describe
(
filter
,
()
=>
{
let
filterId
;
let
optionId
;
let
routePushSpy
;
beforeEach
(()
=>
{
filters
=
initFirstClassVulnerabilityFilters
(
projects
);
filterId
=
filters
[
index
].
id
;
optionId
=
filters
[
index
].
options
[
1
].
id
;
wrapper
=
createComponent
({
propsData
:
{
projects
}
});
routePushSpy
=
jest
.
spyOn
(
router
,
'
push
'
);
selector
().
vm
.
$emit
(
'
setFilter
'
,
{
optionId
,
filterId
});
});
afterEach
(()
=>
{
// This will reset the query state
router
.
push
(
'
/
'
);
});
it
(
'
should set the filter
'
,
()
=>
{
expect
(
selector
().
props
(
'
filter
'
).
selection
).
toEqual
(
new
Set
([
optionId
]));
});
it
(
'
should emit a filterChange event
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
});
await
wrapper
.
vm
.
$nextTick
();
it
(
'
should update the path
'
,
()
=>
{
expect
(
routePushSpy
).
toHaveBeenCalledWith
({
query
:
{
[
filterId
]:
[
optionId
]
},
});
});
expect
(
findProjectFilter
().
props
(
'
filter
'
).
options
).
toEqual
([
{
id
:
'
11
'
,
name
:
projects
[
0
].
name
},
{
id
:
'
12
'
,
name
:
projects
[
1
].
name
},
]);
});
});
});
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