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
0d0b5c9f
Commit
0d0b5c9f
authored
May 28, 2020
by
Savas Vedova
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Persist filters while navigating
- Sync the url with filters - Add tests
parent
bf0c8164
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
150 additions
and
32 deletions
+150
-32
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
...ashboard/components/first_class_vulnerability_filters.vue
+41
-10
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
...oard/components/first_class_vulnerability_filters_spec.js
+109
-22
No files found.
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
View file @
0d0b5c9f
<
script
>
import
{
i
nitFirstClassVulnerabilityFilters
,
mapProjects
}
from
'
ee/security_dashboard/helpers
'
;
import
{
i
sEqual
}
from
'
lodash
'
;
import
{
ALL
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
setFilter
}
from
'
ee/security_dashboard/store/modules/filters/utils
'
;
import
DashboardFilter
from
'
ee/security_dashboard/components/filter.vue
'
;
import
{
initFirstClassVulnerabilityFilters
,
mapProjects
}
from
'
ee/security_dashboard/helpers
'
;
export
default
{
components
:
{
...
...
@@ -16,6 +17,17 @@ export default {
filters
:
initFirstClassVulnerabilityFilters
(
this
.
projects
),
};
},
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
...
...
@@ -28,19 +40,38 @@ export default {
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
);
}
},
},
},
methods
:
{
setFilter
(
options
)
{
const
selectedFilters
=
{};
this
.
filters
=
setFilter
(
this
.
filters
,
options
);
this
.
filters
.
forEach
(({
id
,
selection
})
=>
{
if
(
!
selection
.
has
(
ALL
))
{
selectedFilters
[
id
]
=
Array
.
from
(
selection
);
}
});
this
.
$emit
(
'
filterChange
'
,
selectedFilters
);
this
.
$router
.
push
({
query
:
this
.
selectedFilters
});
this
.
$emit
(
'
filterChange
'
,
this
.
selectedFilters
);
},
},
};
...
...
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
View file @
0d0b5c9f
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
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
Filter
from
'
ee/security_dashboard/components/filter.vue
'
;
const
router
=
new
VueRouter
();
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueRouter
);
describe
(
'
First class vulnerability filters component
'
,
()
=>
{
let
wrapper
;
let
filters
;
const
projects
=
[
{
id
:
'
gid://gitlab/Project/11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
gid://gitlab/Project/12
'
,
name
:
'
GitLab Com
'
},
];
const
findFilters
=
()
=>
wrapper
.
findAll
(
Filter
);
const
findFirstFilter
=
()
=>
findFilters
().
at
(
0
);
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
createComponent
=
({
propsData
}
=
{})
=>
{
return
shallowMount
(
Filters
,
{
propsData
});
const
createComponent
=
({
propsData
,
listeners
}
=
{})
=>
{
return
shallowMount
(
Filters
,
{
localVue
,
router
,
propsData
,
listeners
});
};
afterEach
(()
=>
{
...
...
@@ -35,7 +44,7 @@ describe('First class vulnerability filters component', () => {
});
it
(
'
should pass down the filter information to the first filter
'
,
()
=>
{
expect
(
find
First
Filter
().
props
().
filter
).
toEqual
(
filters
[
0
]);
expect
(
find
State
Filter
().
props
().
filter
).
toEqual
(
filters
[
0
]);
});
it
(
'
should call the setFilter mutation when setting a filter
'
,
()
=>
{
...
...
@@ -43,7 +52,7 @@ describe('First class vulnerability filters component', () => {
const
options
=
{
foo
:
'
bar
'
};
wrapper
.
setMethods
({
setFilter
:
stub
});
find
First
Filter
().
vm
.
$emit
(
'
setFilter
'
,
options
);
find
State
Filter
().
vm
.
$emit
(
'
setFilter
'
,
options
);
expect
(
stub
).
toHaveBeenCalledWith
(
options
);
});
...
...
@@ -99,27 +108,105 @@ describe('First class vulnerability filters component', () => {
});
});
describe
(
'
when setFilter is called
'
,
()
=>
{
let
filterId
;
let
optionId
;
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
},
});
router
.
push
({
query
:
{
[
filter
]:
value
.
split
(
'
,
'
)
}
});
});
beforeEach
(()
=>
{
filterId
=
filters
[
0
].
id
;
optionId
=
filters
[
0
].
options
[
1
].
id
;
wrapper
=
createComponent
();
wrapper
.
vm
.
setFilter
({
filterId
,
optionId
});
});
it
(
`should have the
${
filter
}
filter as pre-selected`
,
()
=>
{
expect
(
selector
().
props
(
'
filter
'
).
selection
).
toEqual
(
new
Set
(
value
.
split
(
'
,
'
)));
});
it
(
'
should set the filters locally
'
,
()
=>
{
const
expectedFilters
=
initFirstClassVulnerabilityFilters
();
expectedFilters
[
0
].
selection
=
new
Set
([
optionId
]);
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
,
{});
});
});
expect
(
wrapper
.
vm
.
filters
).
toEqual
(
expectedFilters
);
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 selected filters when a filter is set
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
expect
(
wrapper
.
emitted
().
filterChange
[
0
]).
toEqual
([{
[
filterId
]:
[
optionId
]
}]);
it
(
'
should emit a filterChange event
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
});
it
(
'
should update the path
'
,
()
=>
{
expect
(
routePushSpy
).
toHaveBeenCalledWith
({
query
:
{
[
filterId
]:
[
optionId
]
},
});
});
});
});
});
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