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
d9a9dced
Commit
d9a9dced
authored
Dec 11, 2019
by
Simon Knox
Committed by
Kushal Pandya
Dec 11, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add recent searches to Sentry error list
Add dropdown and recent text values save values to localStorage
parent
7156c762
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
347 additions
and
81 deletions
+347
-81
app/assets/javascripts/error_tracking/components/error_tracking_list.vue
...scripts/error_tracking/components/error_tracking_list.vue
+137
-73
app/assets/javascripts/error_tracking/store/list/actions.js
app/assets/javascripts/error_tracking/store/list/actions.js
+16
-0
app/assets/javascripts/error_tracking/store/list/mutation_types.js
...s/javascripts/error_tracking/store/list/mutation_types.js
+4
-0
app/assets/javascripts/error_tracking/store/list/mutations.js
...assets/javascripts/error_tracking/store/list/mutations.js
+36
-0
app/assets/javascripts/error_tracking/store/list/state.js
app/assets/javascripts/error_tracking/store/list/state.js
+2
-0
app/assets/stylesheets/framework/filters.scss
app/assets/stylesheets/framework/filters.scss
+1
-1
changelogs/unreleased/34067-add-recent-searches-to-sentry-error-list-in-gitlab.yml
...67-add-recent-searches-to-sentry-error-list-in-gitlab.yml
+5
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/error_tracking/components/error_tracking_list_spec.js
...end/error_tracking/components/error_tracking_list_spec.js
+61
-7
spec/frontend/error_tracking/store/list/mutation_spec.js
spec/frontend/error_tracking/store/list/mutation_spec.js
+82
-0
No files found.
app/assets/javascripts/error_tracking/components/error_tracking_list.vue
View file @
d9a9dced
...
...
@@ -3,11 +3,17 @@ import { mapActions, mapState } from 'vuex';
import
{
GlEmptyState
,
GlButton
,
GlIcon
,
GlLink
,
GlLoadingIcon
,
GlTable
,
GlSearchBoxByClick
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
GlTooltipDirective
,
}
from
'
@gitlab/ui
'
;
import
AccessorUtils
from
'
~/lib/utils/accessor
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
TimeAgo
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
{
__
}
from
'
~/locale
'
;
...
...
@@ -24,14 +30,19 @@ export default {
components
:
{
GlEmptyState
,
GlButton
,
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
GlIcon
,
GlLink
,
GlLoadingIcon
,
GlTable
,
Gl
SearchBoxByClick
,
Gl
FormInput
,
Icon
,
TimeAgo
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
TrackEvent
:
TrackEventDirective
,
},
props
:
{
...
...
@@ -56,13 +67,14 @@ export default {
required
:
true
,
},
},
hasLocalStorage
:
AccessorUtils
.
isLocalStorageAccessSafe
(),
data
()
{
return
{
errorSearchQuery
:
''
,
};
},
computed
:
{
...
mapState
(
'
list
'
,
[
'
errors
'
,
'
externalUrl
'
,
'
loading
'
]),
...
mapState
(
'
list
'
,
[
'
errors
'
,
'
externalUrl
'
,
'
loading
'
,
'
recentSearches
'
]),
},
created
()
{
if
(
this
.
errorTrackingEnabled
)
{
...
...
@@ -70,9 +82,23 @@ export default {
}
},
methods
:
{
...
mapActions
(
'
list
'
,
[
'
startPolling
'
,
'
restartPolling
'
]),
...
mapActions
(
'
list
'
,
[
'
startPolling
'
,
'
restartPolling
'
,
'
addRecentSearch
'
,
'
clearRecentSearches
'
,
'
loadRecentSearches
'
,
'
setIndexPath
'
,
]),
filterErrors
()
{
this
.
startPolling
(
`
${
this
.
indexPath
}
?search_term=
${
this
.
errorSearchQuery
}
`
);
const
searchTerm
=
this
.
errorSearchQuery
.
trim
();
this
.
addRecentSearch
(
searchTerm
);
this
.
startPolling
(
`
${
this
.
indexPath
}
?search_term=
${
searchTerm
}
`
);
},
setSearchText
(
text
)
{
this
.
errorSearchQuery
=
text
;
this
.
filterErrors
();
},
trackViewInSentryOptions
,
getDetailsLink
(
errorId
)
{
...
...
@@ -85,81 +111,119 @@ export default {
<
template
>
<div>
<div
v-if=
"errorTrackingEnabled"
>
<div>
<div
class=
"d-flex flex-row justify-content-around bg-secondary border"
>
<gl-search-box-by-click
v-model=
"errorSearchQuery"
class=
"col-lg-10 m-3 p-0"
:placeholder=
"__('Search or filter results...')"
type=
"search"
autofocus
@
submit=
"filterErrors"
/>
<gl-button
v-track-event=
"trackViewInSentryOptions(externalUrl)"
class=
"m-3"
variant=
"primary"
:href=
"externalUrl"
target=
"_blank"
<div
class=
"d-flex flex-row justify-content-around bg-secondary border p-3"
>
<div
class=
"filtered-search-box"
>
<gl-dropdown
:text=
"__('Recent searches')"
class=
"filtered-search-history-dropdown-wrapper d-none d-md-block"
toggle-class=
"filtered-search-history-dropdown-toggle-button"
:disabled=
"loading"
>
{{
__
(
'
View in Sentry
'
)
}}
<icon
name=
"external-link"
class=
"flex-shrink-0"
/>
</gl-button>
</div>
<div
v-if=
"loading"
class=
"py-3"
>
<gl-loading-icon
size=
"md"
/>
<div
v-if=
"!$options.hasLocalStorage"
class=
"px-3"
>
{{
__
(
'
This feature requires local storage to be enabled
'
)
}}
</div>
<template
v-else-if=
"recentSearches.length > 0"
>
<gl-dropdown-item
v-for=
"searchQuery in recentSearches"
:key=
"searchQuery"
@
click=
"setSearchText(searchQuery)"
>
{{
searchQuery
}}
</gl-dropdown-item
>
<gl-dropdown-divider
/>
<gl-dropdown-item
ref=
"clearRecentSearches"
@
click=
"clearRecentSearches"
>
{{
__
(
'
Clear recent searches
'
)
}}
</gl-dropdown-item>
</
template
>
<div
v-else
class=
"px-3"
>
{{ __("You don't have any recent searches") }}
</div>
</gl-dropdown>
<div
class=
"filtered-search-input-container flex-fill"
>
<gl-form-input
v-model=
"errorSearchQuery"
class=
"pl-2 filtered-search"
:disabled=
"loading"
:placeholder=
"__('Search or filter results…')"
autofocus
@
keyup.enter.native=
"filterErrors"
/>
</div>
<div
class=
"gl-search-box-by-type-right-icons"
>
<gl-button
v-if=
"errorSearchQuery.length > 0"
v-gl-tooltip
.
hover
:title=
"__('Clear')"
class=
"clear-search text-secondary"
name=
"clear"
@
click=
"errorSearchQuery = ''"
>
<gl-icon
name=
"close"
:size=
"12"
/>
</gl-button>
</div>
</div>
<gl-table
v-else
class=
"mt-3"
:items=
"errors"
:fields=
"$options.fields"
:show-empty=
"true"
fixed
stacked=
"sm"
<gl-button
v-track-event=
"trackViewInSentryOptions(externalUrl)"
class=
"ml-3"
variant=
"primary"
:href=
"externalUrl"
target=
"_blank"
>
<template
slot=
"HEAD_events"
slot-scope=
"data"
>
<div
class=
"text-md-right"
>
{{
data
.
label
}}
</div>
</
template
>
<
template
slot=
"HEAD_users"
slot-scope=
"data"
>
<div
class=
"text-md-right"
>
{{
data
.
label
}}
</div>
</
template
>
<
template
slot=
"error"
slot-scope=
"errors"
>
<div
class=
"d-flex flex-column"
>
<gl-link
class=
"d-flex text-dark"
:href=
"getDetailsLink(errors.item.id)"
>
<strong
class=
"text-truncate"
>
{{
errors
.
item
.
title
.
trim
()
}}
</strong>
</gl-link>
<span
class=
"text-secondary text-truncate"
>
{{
errors
.
item
.
culprit
}}
</span>
</div>
</
template
>
{{ __('View in Sentry') }}
<icon
name=
"external-link"
class=
"flex-shrink-0"
/>
</gl-button>
</div>
<
template
slot=
"events"
slot-scope=
"errors
"
>
<div
class=
"text-md-right"
>
{{
errors
.
item
.
count
}}
</div
>
</
template
>
<div
v-if=
"loading"
class=
"py-3
"
>
<gl-loading-icon
size=
"md"
/
>
</div
>
<
template
slot=
"users"
slot-scope=
"errors"
>
<div
class=
"text-md-right"
>
{{
errors
.
item
.
userCount
}}
</div>
</
template
>
<gl-table
v-else
class=
"mt-3"
:items=
"errors"
:fields=
"$options.fields"
:show-empty=
"true"
fixed
stacked=
"sm"
>
<
template
slot=
"HEAD_events"
slot-scope=
"data"
>
<div
class=
"text-md-right"
>
{{
data
.
label
}}
</div>
</
template
>
<
template
slot=
"HEAD_users"
slot-scope=
"data"
>
<div
class=
"text-md-right"
>
{{
data
.
label
}}
</div>
</
template
>
<
template
slot=
"error"
slot-scope=
"errors"
>
<div
class=
"d-flex flex-column"
>
<gl-link
class=
"d-flex text-dark"
:href=
"getDetailsLink(errors.item.id)"
>
<strong
class=
"text-truncate"
>
{{
errors
.
item
.
title
.
trim
()
}}
</strong>
</gl-link>
<span
class=
"text-secondary text-truncate"
>
{{
errors
.
item
.
culprit
}}
</span>
</div>
</
template
>
<
template
slot=
"lastSeen"
slot-scope=
"errors"
>
<div
class=
"d-flex align-items-center"
>
<time-ago
:time=
"errors.item.lastSeen"
class=
"text-secondary"
/>
</div>
</
template
>
<
template
slot=
"empty"
>
<div
ref=
"empty"
>
{{
__
(
'
No errors to display.
'
)
}}
<gl-link
class=
"js-try-again"
@
click=
"restartPolling"
>
{{
__
(
'
Check again
'
)
}}
</gl-link>
</div>
</
template
>
</gl-table>
</div>
<
template
slot=
"events"
slot-scope=
"errors"
>
<div
class=
"text-md-right"
>
{{
errors
.
item
.
count
}}
</div>
</
template
>
<
template
slot=
"users"
slot-scope=
"errors"
>
<div
class=
"text-md-right"
>
{{
errors
.
item
.
userCount
}}
</div>
</
template
>
<
template
slot=
"lastSeen"
slot-scope=
"errors"
>
<div
class=
"d-flex align-items-center"
>
<time-ago
:time=
"errors.item.lastSeen"
class=
"text-secondary"
/>
</div>
</
template
>
<
template
slot=
"empty"
>
<div
ref=
"empty"
>
{{
__
(
'
No errors to display.
'
)
}}
<gl-link
class=
"js-try-again"
@
click=
"restartPolling"
>
{{
__
(
'
Check again
'
)
}}
</gl-link>
</div>
</
template
>
</gl-table>
</div>
<div
v-else-if=
"userCanEnableErrorTracking"
>
<gl-empty-state
...
...
app/assets/javascripts/error_tracking/store/list/actions.js
View file @
d9a9dced
...
...
@@ -51,4 +51,20 @@ export function restartPolling({ commit }) {
if
(
eTagPoll
)
eTagPoll
.
restart
();
}
export
function
setIndexPath
({
commit
},
path
)
{
commit
(
types
.
SET_INDEX_PATH
,
path
);
}
export
function
loadRecentSearches
({
commit
})
{
commit
(
types
.
LOAD_RECENT_SEARCHES
);
}
export
function
addRecentSearch
({
commit
},
searchQuery
)
{
commit
(
types
.
ADD_RECENT_SEARCH
,
searchQuery
);
}
export
function
clearRecentSearches
({
commit
})
{
commit
(
types
.
CLEAR_RECENT_SEARCHES
);
}
export
default
()
=>
{};
app/assets/javascripts/error_tracking/store/list/mutation_types.js
View file @
d9a9dced
export
const
SET_ERRORS
=
'
SET_ERRORS
'
;
export
const
SET_EXTERNAL_URL
=
'
SET_EXTERNAL_URL
'
;
export
const
SET_INDEX_PATH
=
'
SET_INDEX_PATH
'
;
export
const
SET_LOADING
=
'
SET_LOADING
'
;
export
const
ADD_RECENT_SEARCH
=
'
ADD_RECENT_SEARCH
'
;
export
const
CLEAR_RECENT_SEARCHES
=
'
CLEAR_RECENT_SEARCHES
'
;
export
const
LOAD_RECENT_SEARCHES
=
'
LOAD_RECENT_SEARCHES
'
;
app/assets/javascripts/error_tracking/store/list/mutations.js
View file @
d9a9dced
import
*
as
types
from
'
./mutation_types
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
AccessorUtils
from
'
~/lib/utils/accessor
'
;
export
default
{
[
types
.
SET_ERRORS
](
state
,
data
)
{
...
...
@@ -11,4 +12,39 @@ export default {
[
types
.
SET_LOADING
](
state
,
loading
)
{
state
.
loading
=
loading
;
},
[
types
.
SET_INDEX_PATH
](
state
,
path
)
{
state
.
indexPath
=
path
;
},
[
types
.
ADD_RECENT_SEARCH
](
state
,
searchTerm
)
{
if
(
searchTerm
.
length
===
0
)
{
return
;
}
// remove any existing item, then add it to the start of the list
const
recentSearches
=
state
.
recentSearches
.
filter
(
s
=>
s
!==
searchTerm
);
recentSearches
.
unshift
(
searchTerm
);
// only keep the last 5
state
.
recentSearches
=
recentSearches
.
slice
(
0
,
5
);
if
(
AccessorUtils
.
isLocalStorageAccessSafe
())
{
localStorage
.
setItem
(
`recent-searches
${
state
.
indexPath
}
`
,
JSON
.
stringify
(
state
.
recentSearches
),
);
}
},
[
types
.
CLEAR_RECENT_SEARCHES
](
state
)
{
state
.
recentSearches
=
[];
if
(
AccessorUtils
.
isLocalStorageAccessSafe
())
{
localStorage
.
removeItem
(
`recent-searches
${
state
.
indexPath
}
`
);
}
},
[
types
.
LOAD_RECENT_SEARCHES
](
state
)
{
const
recentSearches
=
localStorage
.
getItem
(
`recent-searches
${
state
.
indexPath
}
`
)
||
[];
try
{
state
.
recentSearches
=
JSON
.
parse
(
recentSearches
);
}
catch
(
e
)
{
state
.
recentSearches
=
[];
throw
e
;
}
},
};
app/assets/javascripts/error_tracking/store/list/state.js
View file @
d9a9dced
...
...
@@ -2,4 +2,6 @@ export default () => ({
errors
:
[],
externalUrl
:
''
,
loading
:
true
,
indexPath
:
''
,
recentSearches
:
[],
});
app/assets/stylesheets/framework/filters.scss
View file @
d9a9dced
...
...
@@ -214,8 +214,8 @@
padding-left
:
0
;
height
:
$input-height
-
2
;
line-height
:
inherit
;
border-color
:
transparent
;
&
,
&
:focus
,
&
:hover
{
outline
:
none
;
...
...
changelogs/unreleased/34067-add-recent-searches-to-sentry-error-list-in-gitlab.yml
0 → 100644
View file @
d9a9dced
---
title
:
Add recent search to error tracking
merge_request
:
19301
author
:
type
:
added
locale/gitlab.pot
View file @
d9a9dced
...
...
@@ -15349,6 +15349,9 @@ msgstr ""
msgid "Search or filter results..."
msgstr ""
msgid "Search or filter results…"
msgstr ""
msgid "Search or jump to…"
msgstr ""
...
...
spec/frontend/error_tracking/components/error_tracking_list_spec.js
View file @
d9a9dced
...
...
@@ -6,8 +6,11 @@ import {
GlLoadingIcon
,
GlTable
,
GlLink
,
GlSearchBoxByClick
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
}
from
'
@gitlab/ui
'
;
import
createListState
from
'
~/error_tracking/store/list/state
'
;
import
ErrorTrackingList
from
'
~/error_tracking/components/error_tracking_list.vue
'
;
import
errorsList
from
'
./list_mock.json
'
;
...
...
@@ -51,12 +54,13 @@ describe('ErrorTrackingList', () => {
getErrorList
:
()
=>
{},
startPolling
:
jest
.
fn
(),
restartPolling
:
jest
.
fn
().
mockName
(
'
restartPolling
'
),
addRecentSearch
:
jest
.
fn
(),
loadRecentSearches
:
jest
.
fn
(),
setIndexPath
:
jest
.
fn
(),
clearRecentSearches
:
jest
.
fn
(),
};
const
state
=
{
errors
:
errorsList
,
loading
:
true
,
};
const
state
=
createListState
();
store
=
new
Vuex
.
Store
({
modules
:
{
...
...
@@ -90,6 +94,7 @@ describe('ErrorTrackingList', () => {
describe
(
'
results
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
list
.
loading
=
false
;
store
.
state
.
list
.
errors
=
errorsList
;
mountComponent
();
});
...
...
@@ -114,7 +119,7 @@ describe('ErrorTrackingList', () => {
});
describe
(
'
filtering
'
,
()
=>
{
const
findSearchBox
=
()
=>
wrapper
.
find
(
Gl
SearchBoxByClick
);
const
findSearchBox
=
()
=>
wrapper
.
find
(
Gl
FormInput
);
it
(
'
shows search box
'
,
()
=>
{
expect
(
findSearchBox
().
exists
()).
toBe
(
true
);
...
...
@@ -122,7 +127,9 @@ describe('ErrorTrackingList', () => {
it
(
'
makes network request on submit
'
,
()
=>
{
expect
(
actions
.
startPolling
).
toHaveBeenCalledTimes
(
1
);
findSearchBox
().
vm
.
$emit
(
'
submit
'
);
findSearchBox
().
trigger
(
'
keyup.enter
'
);
expect
(
actions
.
startPolling
).
toHaveBeenCalledTimes
(
2
);
});
});
...
...
@@ -185,4 +192,51 @@ describe('ErrorTrackingList', () => {
);
});
});
describe
(
'
recent searches
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
shows empty message
'
,
()
=>
{
store
.
state
.
list
.
recentSearches
=
[];
expect
(
wrapper
.
find
(
GlDropdown
).
text
()).
toBe
(
"
You don't have any recent searches
"
);
});
it
(
'
shows items
'
,
()
=>
{
store
.
state
.
list
.
recentSearches
=
[
'
great
'
,
'
search
'
];
const
dropdownItems
=
wrapper
.
findAll
(
GlDropdownItem
);
expect
(
dropdownItems
.
length
).
toBe
(
3
);
expect
(
dropdownItems
.
at
(
0
).
text
()).
toBe
(
'
great
'
);
expect
(
dropdownItems
.
at
(
1
).
text
()).
toBe
(
'
search
'
);
});
describe
(
'
clear
'
,
()
=>
{
const
clearRecentButton
=
()
=>
wrapper
.
find
({
ref
:
'
clearRecentSearches
'
});
it
(
'
is hidden when list empty
'
,
()
=>
{
store
.
state
.
list
.
recentSearches
=
[];
expect
(
clearRecentButton
().
exists
()).
toBe
(
false
);
});
it
(
'
is visible when list has items
'
,
()
=>
{
store
.
state
.
list
.
recentSearches
=
[
'
some
'
,
'
searches
'
];
expect
(
clearRecentButton
().
exists
()).
toBe
(
true
);
expect
(
clearRecentButton
().
text
()).
toBe
(
'
Clear recent searches
'
);
});
it
(
'
clears items on click
'
,
()
=>
{
store
.
state
.
list
.
recentSearches
=
[
'
some
'
,
'
searches
'
];
clearRecentButton
().
vm
.
$emit
(
'
click
'
);
expect
(
actions
.
clearRecentSearches
).
toHaveBeenCalledTimes
(
1
);
});
});
});
});
spec/frontend/error_tracking/store/list/mutation_spec.js
View file @
d9a9dced
import
mutations
from
'
~/error_tracking/store/list/mutations
'
;
import
*
as
types
from
'
~/error_tracking/store/list/mutation_types
'
;
import
{
useLocalStorageSpy
}
from
'
helpers/local_storage_helper
'
;
const
ADD_RECENT_SEARCH
=
mutations
[
types
.
ADD_RECENT_SEARCH
];
const
CLEAR_RECENT_SEARCHES
=
mutations
[
types
.
CLEAR_RECENT_SEARCHES
];
const
LOAD_RECENT_SEARCHES
=
mutations
[
types
.
LOAD_RECENT_SEARCHES
];
describe
(
'
Error tracking mutations
'
,
()
=>
{
describe
(
'
SET_ERRORS
'
,
()
=>
{
...
...
@@ -33,4 +38,81 @@ describe('Error tracking mutations', () => {
});
});
});
describe
(
'
recent searches
'
,
()
=>
{
useLocalStorageSpy
();
let
state
;
beforeEach
(()
=>
{
state
=
{
indexPath
:
'
/project/errors.json
'
,
recentSearches
:
[],
};
});
describe
(
'
ADD_RECENT_SEARCH
'
,
()
=>
{
it
(
'
adds search queries to recentSearches and localStorage
'
,
()
=>
{
ADD_RECENT_SEARCH
(
state
,
'
my issue
'
);
expect
(
state
.
recentSearches
).
toEqual
([
'
my issue
'
]);
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
'
recent-searches/project/errors.json
'
,
'
["my issue"]
'
,
);
});
it
(
'
does not add empty searches
'
,
()
=>
{
ADD_RECENT_SEARCH
(
state
,
''
);
expect
(
state
.
recentSearches
).
toEqual
([]);
expect
(
localStorage
.
setItem
).
not
.
toHaveBeenCalled
();
});
it
(
'
adds new queries to start of the list
'
,
()
=>
{
state
.
recentSearches
=
[
'
previous
'
,
'
searches
'
];
ADD_RECENT_SEARCH
(
state
,
'
new search
'
);
expect
(
state
.
recentSearches
).
toEqual
([
'
new search
'
,
'
previous
'
,
'
searches
'
]);
});
it
(
'
limits recentSearches to 5 items
'
,
()
=>
{
state
.
recentSearches
=
[
1
,
2
,
3
,
4
,
5
];
ADD_RECENT_SEARCH
(
state
,
'
new search
'
);
expect
(
state
.
recentSearches
).
toEqual
([
'
new search
'
,
1
,
2
,
3
,
4
]);
});
it
(
'
does not add same search query twice
'
,
()
=>
{
state
.
recentSearches
=
[
'
already
'
,
'
searched
'
];
ADD_RECENT_SEARCH
(
state
,
'
searched
'
);
expect
(
state
.
recentSearches
).
toEqual
([
'
searched
'
,
'
already
'
]);
});
});
describe
(
'
CLEAR_RECENT_SEARCHES
'
,
()
=>
{
it
(
'
clears recentSearches and localStorage
'
,
()
=>
{
state
.
recentSearches
=
[
'
first
'
,
'
second
'
];
CLEAR_RECENT_SEARCHES
(
state
);
expect
(
state
.
recentSearches
).
toEqual
([]);
expect
(
localStorage
.
removeItem
).
toHaveBeenCalledWith
(
'
recent-searches/project/errors.json
'
);
});
});
describe
(
'
LOAD_RECENT_SEARCHES
'
,
()
=>
{
it
(
'
loads recent searches from localStorage
'
,
()
=>
{
jest
.
spyOn
(
window
.
localStorage
,
'
getItem
'
).
mockReturnValue
(
'
["first", "second"]
'
);
LOAD_RECENT_SEARCHES
(
state
);
expect
(
state
.
recentSearches
).
toEqual
([
'
first
'
,
'
second
'
]);
expect
(
localStorage
.
getItem
).
toHaveBeenCalledWith
(
'
recent-searches/project/errors.json
'
);
});
});
});
});
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