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
cb98fe72
Commit
cb98fe72
authored
Jul 14, 2021
by
Zack Cuddy
Committed by
Andrew Fontaine
Jul 14, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Global Search - Recent Groups/Projects
parent
59abe43f
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
191 additions
and
49 deletions
+191
-49
app/assets/javascripts/search/store/getters.js
app/assets/javascripts/search/store/getters.js
+9
-0
app/assets/javascripts/search/store/index.js
app/assets/javascripts/search/store/index.js
+2
-0
app/assets/javascripts/search/store/utils.js
app/assets/javascripts/search/store/utils.js
+20
-4
app/assets/javascripts/search/topbar/components/group_filter.vue
...ets/javascripts/search/topbar/components/group_filter.vue
+3
-1
app/assets/javascripts/search/topbar/components/project_filter.vue
...s/javascripts/search/topbar/components/project_filter.vue
+3
-1
app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
...ascripts/search/topbar/components/searchable_dropdown.vue
+33
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/search/store/getters_spec.js
spec/frontend/search/store/getters_spec.js
+32
-0
spec/frontend/search/store/utils_spec.js
spec/frontend/search/store/utils_spec.js
+35
-26
spec/frontend/search/topbar/components/group_filter_spec.js
spec/frontend/search/topbar/components/group_filter_spec.js
+3
-0
spec/frontend/search/topbar/components/project_filter_spec.js
.../frontend/search/topbar/components/project_filter_spec.js
+3
-0
spec/frontend/search/topbar/components/searchable_dropdown_spec.js
...tend/search/topbar/components/searchable_dropdown_spec.js
+45
-17
No files found.
app/assets/javascripts/search/store/getters.js
0 → 100644
View file @
cb98fe72
import
{
GROUPS_LOCAL_STORAGE_KEY
,
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
./constants
'
;
export
const
frequentGroups
=
(
state
)
=>
{
return
state
.
frequentItems
[
GROUPS_LOCAL_STORAGE_KEY
];
};
export
const
frequentProjects
=
(
state
)
=>
{
return
state
.
frequentItems
[
PROJECTS_LOCAL_STORAGE_KEY
];
};
app/assets/javascripts/search/store/index.js
View file @
cb98fe72
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
*
as
actions
from
'
./actions
'
;
import
*
as
actions
from
'
./actions
'
;
import
*
as
getters
from
'
./getters
'
;
import
mutations
from
'
./mutations
'
;
import
mutations
from
'
./mutations
'
;
import
createState
from
'
./state
'
;
import
createState
from
'
./state
'
;
...
@@ -8,6 +9,7 @@ Vue.use(Vuex);
...
@@ -8,6 +9,7 @@ Vue.use(Vuex);
export
const
getStoreConfig
=
({
query
})
=>
({
export
const
getStoreConfig
=
({
query
})
=>
({
actions
,
actions
,
getters
,
mutations
,
mutations
,
state
:
createState
({
query
}),
state
:
createState
({
query
}),
});
});
...
...
app/assets/javascripts/search/store/utils.js
View file @
cb98fe72
...
@@ -24,7 +24,15 @@ export const setFrequentItemToLS = (key, data, itemData) => {
...
@@ -24,7 +24,15 @@ export const setFrequentItemToLS = (key, data, itemData) => {
return
;
return
;
}
}
const
keyList
=
[
'
id
'
,
'
avatar_url
'
,
'
name
'
,
'
full_name
'
,
'
name_with_namespace
'
,
'
frequency
'
];
const
keyList
=
[
'
id
'
,
'
avatar_url
'
,
'
name
'
,
'
full_name
'
,
'
name_with_namespace
'
,
'
frequency
'
,
'
lastUsed
'
,
];
try
{
try
{
const
frequentItems
=
data
[
key
].
map
((
obj
)
=>
extractKeys
(
obj
,
keyList
));
const
frequentItems
=
data
[
key
].
map
((
obj
)
=>
extractKeys
(
obj
,
keyList
));
...
@@ -35,17 +43,25 @@ export const setFrequentItemToLS = (key, data, itemData) => {
...
@@ -35,17 +43,25 @@ export const setFrequentItemToLS = (key, data, itemData) => {
// Up the frequency (Max 5)
// Up the frequency (Max 5)
const
currentFrequency
=
frequentItems
[
existingItemIndex
].
frequency
;
const
currentFrequency
=
frequentItems
[
existingItemIndex
].
frequency
;
frequentItems
[
existingItemIndex
].
frequency
=
Math
.
min
(
currentFrequency
+
1
,
MAX_FREQUENCY
);
frequentItems
[
existingItemIndex
].
frequency
=
Math
.
min
(
currentFrequency
+
1
,
MAX_FREQUENCY
);
frequentItems
[
existingItemIndex
].
lastUsed
=
new
Date
().
getTime
();
}
else
{
}
else
{
// Only store a max of 5 items
// Only store a max of 5 items
if
(
frequentItems
.
length
>=
MAX_FREQUENT_ITEMS
)
{
if
(
frequentItems
.
length
>=
MAX_FREQUENT_ITEMS
)
{
frequentItems
.
pop
();
frequentItems
.
pop
();
}
}
frequentItems
.
push
({
...
item
,
frequency
:
1
});
frequentItems
.
push
({
...
item
,
frequency
:
1
,
lastUsed
:
new
Date
().
getTime
()
});
}
}
// Sort by frequency
// Sort by frequency and lastUsed
frequentItems
.
sort
((
a
,
b
)
=>
b
.
frequency
-
a
.
frequency
);
frequentItems
.
sort
((
a
,
b
)
=>
{
if
(
a
.
frequency
>
b
.
frequency
)
{
return
-
1
;
}
else
if
(
a
.
frequency
<
b
.
frequency
)
{
return
1
;
}
return
b
.
lastUsed
-
a
.
lastUsed
;
});
// Note we do not need to commit a mutation here as immediately after this we refresh the page to
// Note we do not need to commit a mutation here as immediately after this we refresh the page to
// update the search results.
// update the search results.
...
...
app/assets/javascripts/search/topbar/components/group_filter.vue
View file @
cb98fe72
<
script
>
<
script
>
import
{
isEmpty
}
from
'
lodash
'
;
import
{
isEmpty
}
from
'
lodash
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
../constants
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
../constants
'
;
import
SearchableDropdown
from
'
./searchable_dropdown.vue
'
;
import
SearchableDropdown
from
'
./searchable_dropdown.vue
'
;
...
@@ -19,6 +19,7 @@ export default {
...
@@ -19,6 +19,7 @@ export default {
},
},
computed
:
{
computed
:
{
...
mapState
([
'
groups
'
,
'
fetchingGroups
'
]),
...
mapState
([
'
groups
'
,
'
fetchingGroups
'
]),
...
mapGetters
([
'
frequentGroups
'
]),
selectedGroup
()
{
selectedGroup
()
{
return
isEmpty
(
this
.
initialData
)
?
ANY_OPTION
:
this
.
initialData
;
return
isEmpty
(
this
.
initialData
)
?
ANY_OPTION
:
this
.
initialData
;
},
},
...
@@ -49,6 +50,7 @@ export default {
...
@@ -49,6 +50,7 @@ export default {
:loading=
"fetchingGroups"
:loading=
"fetchingGroups"
:selected-item=
"selectedGroup"
:selected-item=
"selectedGroup"
:items=
"groups"
:items=
"groups"
:frequent-items=
"frequentGroups"
@
first-open=
"loadFrequentGroups"
@
first-open=
"loadFrequentGroups"
@
search=
"fetchGroups"
@
search=
"fetchGroups"
@
change=
"handleGroupChange"
@
change=
"handleGroupChange"
...
...
app/assets/javascripts/search/topbar/components/project_filter.vue
View file @
cb98fe72
<
script
>
<
script
>
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
../constants
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
../constants
'
;
import
SearchableDropdown
from
'
./searchable_dropdown.vue
'
;
import
SearchableDropdown
from
'
./searchable_dropdown.vue
'
;
...
@@ -18,6 +18,7 @@ export default {
...
@@ -18,6 +18,7 @@ export default {
},
},
computed
:
{
computed
:
{
...
mapState
([
'
projects
'
,
'
fetchingProjects
'
]),
...
mapState
([
'
projects
'
,
'
fetchingProjects
'
]),
...
mapGetters
([
'
frequentProjects
'
]),
selectedProject
()
{
selectedProject
()
{
return
this
.
initialData
?
this
.
initialData
:
ANY_OPTION
;
return
this
.
initialData
?
this
.
initialData
:
ANY_OPTION
;
},
},
...
@@ -52,6 +53,7 @@ export default {
...
@@ -52,6 +53,7 @@ export default {
:loading=
"fetchingProjects"
:loading=
"fetchingProjects"
:selected-item=
"selectedProject"
:selected-item=
"selectedProject"
:items=
"projects"
:items=
"projects"
:frequent-items=
"frequentProjects"
@
first-open=
"loadFrequentProjects"
@
first-open=
"loadFrequentProjects"
@
search=
"fetchProjects"
@
search=
"fetchProjects"
@
change=
"handleProjectChange"
@
change=
"handleProjectChange"
...
...
app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
View file @
cb98fe72
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
import
{
import
{
GlDropdown
,
GlDropdown
,
GlDropdownItem
,
GlDropdownItem
,
GlDropdownSectionHeader
,
GlSearchBoxByType
,
GlSearchBoxByType
,
GlLoadingIcon
,
GlLoadingIcon
,
GlIcon
,
GlIcon
,
...
@@ -16,11 +17,13 @@ import SearchableDropdownItem from './searchable_dropdown_item.vue';
...
@@ -16,11 +17,13 @@ import SearchableDropdownItem from './searchable_dropdown_item.vue';
export
default
{
export
default
{
i18n
:
{
i18n
:
{
clearLabel
:
__
(
'
Clear
'
),
clearLabel
:
__
(
'
Clear
'
),
frequentlySearched
:
__
(
'
Frequently searched
'
),
},
},
name
:
'
SearchableDropdown
'
,
name
:
'
SearchableDropdown
'
,
components
:
{
components
:
{
GlDropdown
,
GlDropdown
,
GlDropdownItem
,
GlDropdownItem
,
GlDropdownSectionHeader
,
GlSearchBoxByType
,
GlSearchBoxByType
,
GlLoadingIcon
,
GlLoadingIcon
,
GlIcon
,
GlIcon
,
...
@@ -61,6 +64,11 @@ export default {
...
@@ -61,6 +64,11 @@ export default {
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
[],
},
},
frequentItems
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -68,6 +76,11 @@ export default {
...
@@ -68,6 +76,11 @@ export default {
hasBeenOpened
:
false
,
hasBeenOpened
:
false
,
};
};
},
},
computed
:
{
showFrequentItems
()
{
return
!
this
.
searchText
&&
this
.
frequentItems
.
length
>
0
;
},
},
methods
:
{
methods
:
{
isSelected
(
selected
)
{
isSelected
(
selected
)
{
return
selected
.
id
===
this
.
selectedItem
.
id
;
return
selected
.
id
===
this
.
selectedItem
.
id
;
...
@@ -139,6 +152,25 @@ export default {
...
@@ -139,6 +152,25 @@ export default {
<span
data-testid=
"item-title"
>
{{ $options.ANY_OPTION.name }}
</span>
<span
data-testid=
"item-title"
>
{{ $options.ANY_OPTION.name }}
</span>
</gl-dropdown-item>
</gl-dropdown-item>
</div>
</div>
<div
v-if=
"showFrequentItems"
class=
"gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2 gl-mb-2"
>
<gl-dropdown-section-header>
{{
$options.i18n.frequentlySearched
}}
</gl-dropdown-section-header>
<searchable-dropdown-item
v-for=
"item in frequentItems"
:key=
"item.id"
:item=
"item"
:selected-item=
"selectedItem"
:search-text=
"searchText"
:name=
"name"
:full-name=
"fullName"
data-testid=
"frequent-items"
@
change=
"updateDropdown"
/>
</div>
<div
v-if=
"!loading"
>
<div
v-if=
"!loading"
>
<searchable-dropdown-item
<searchable-dropdown-item
v-for=
"item in items"
v-for=
"item in items"
...
@@ -148,6 +180,7 @@ export default {
...
@@ -148,6 +180,7 @@ export default {
:search-text=
"searchText"
:search-text=
"searchText"
:name=
"name"
:name=
"name"
:full-name=
"fullName"
:full-name=
"fullName"
data-testid=
"searchable-items"
@
change=
"updateDropdown"
@
change=
"updateDropdown"
/>
/>
</div>
</div>
...
...
locale/gitlab.pot
View file @
cb98fe72
...
@@ -14359,6 +14359,9 @@ msgstr ""
...
@@ -14359,6 +14359,9 @@ msgstr ""
msgid "Frequency"
msgid "Frequency"
msgstr ""
msgstr ""
msgid "Frequently searched"
msgstr ""
msgid "Friday"
msgid "Friday"
msgstr ""
msgstr ""
...
...
spec/frontend/search/store/getters_spec.js
0 → 100644
View file @
cb98fe72
import
{
GROUPS_LOCAL_STORAGE_KEY
,
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
~/search/store/constants
'
;
import
*
as
getters
from
'
~/search/store/getters
'
;
import
createState
from
'
~/search/store/state
'
;
import
{
MOCK_QUERY
,
MOCK_GROUPS
,
MOCK_PROJECTS
}
from
'
../mock_data
'
;
describe
(
'
Global Search Store Getters
'
,
()
=>
{
let
state
;
beforeEach
(()
=>
{
state
=
createState
({
query
:
MOCK_QUERY
});
});
describe
(
'
frequentGroups
'
,
()
=>
{
beforeEach
(()
=>
{
state
.
frequentItems
[
GROUPS_LOCAL_STORAGE_KEY
]
=
MOCK_GROUPS
;
});
it
(
'
returns the correct data
'
,
()
=>
{
expect
(
getters
.
frequentGroups
(
state
)).
toStrictEqual
(
MOCK_GROUPS
);
});
});
describe
(
'
frequentProjects
'
,
()
=>
{
beforeEach
(()
=>
{
state
.
frequentItems
[
PROJECTS_LOCAL_STORAGE_KEY
]
=
MOCK_PROJECTS
;
});
it
(
'
returns the correct data
'
,
()
=>
{
expect
(
getters
.
frequentProjects
(
state
)).
toStrictEqual
(
MOCK_PROJECTS
);
});
});
});
spec/frontend/search/store/utils_spec.js
View file @
cb98fe72
...
@@ -9,6 +9,9 @@ import {
...
@@ -9,6 +9,9 @@ import {
STALE_STORED_DATA
,
STALE_STORED_DATA
,
}
from
'
../mock_data
'
;
}
from
'
../mock_data
'
;
const
PREV_TIME
=
new
Date
().
getTime
()
-
1
;
const
CURRENT_TIME
=
new
Date
().
getTime
();
useLocalStorageSpy
();
useLocalStorageSpy
();
jest
.
mock
(
'
~/lib/utils/accessor
'
,
()
=>
({
jest
.
mock
(
'
~/lib/utils/accessor
'
,
()
=>
({
isLocalStorageAccessSafe
:
jest
.
fn
().
mockReturnValue
(
true
),
isLocalStorageAccessSafe
:
jest
.
fn
().
mockReturnValue
(
true
),
...
@@ -52,28 +55,32 @@ describe('Global Search Store Utils', () => {
...
@@ -52,28 +55,32 @@ describe('Global Search Store Utils', () => {
describe
(
'
with existing data
'
,
()
=>
{
describe
(
'
with existing data
'
,
()
=>
{
describe
(
`when frequency is less than
${
MAX_FREQUENCY
}
`
,
()
=>
{
describe
(
`when frequency is less than
${
MAX_FREQUENCY
}
`
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[{
...
MOCK_GROUPS
[
0
],
frequency
:
1
}];
frequentItems
[
MOCK_LS_KEY
]
=
[{
...
MOCK_GROUPS
[
0
],
frequency
:
1
,
lastUsed
:
PREV_TIME
}];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
});
it
(
'
adds 1 to the frequency and calls localStorage.setItem
'
,
()
=>
{
it
(
'
adds 1 to the frequency
, tracks lastUsed,
and calls localStorage.setItem
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
2
}]),
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
2
,
lastUsed
:
CURRENT_TIME
}]),
);
);
});
});
});
});
describe
(
`when frequency is equal to
${
MAX_FREQUENCY
}
`
,
()
=>
{
describe
(
`when frequency is equal to
${
MAX_FREQUENCY
}
`
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[{
...
MOCK_GROUPS
[
0
],
frequency
:
MAX_FREQUENCY
}];
frequentItems
[
MOCK_LS_KEY
]
=
[
{
...
MOCK_GROUPS
[
0
],
frequency
:
MAX_FREQUENCY
,
lastUsed
:
PREV_TIME
},
];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
});
it
(
`does not further increase frequency past
${
MAX_FREQUENCY
}
and calls localStorage.setItem`
,
()
=>
{
it
(
`does not further increase frequency past
${
MAX_FREQUENCY
}
, tracks lastUsed,
and calls localStorage.setItem`
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
MAX_FREQUENCY
}]),
JSON
.
stringify
([
{
...
MOCK_GROUPS
[
0
],
frequency
:
MAX_FREQUENCY
,
lastUsed
:
CURRENT_TIME
},
]),
);
);
});
});
});
});
...
@@ -85,10 +92,10 @@ describe('Global Search Store Utils', () => {
...
@@ -85,10 +92,10 @@ describe('Global Search Store Utils', () => {
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
});
it
(
'
adds a new entry with frequency 1 and calls localStorage.setItem
'
,
()
=>
{
it
(
'
adds a new entry with frequency 1
, tracks lastUsed,
and calls localStorage.setItem
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
1
}]),
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
1
,
lastUsed
:
CURRENT_TIME
}]),
);
);
});
});
});
});
...
@@ -96,18 +103,20 @@ describe('Global Search Store Utils', () => {
...
@@ -96,18 +103,20 @@ describe('Global Search Store Utils', () => {
describe
(
'
with multiple entries
'
,
()
=>
{
describe
(
'
with multiple entries
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[
frequentItems
[
MOCK_LS_KEY
]
=
[
{
...
MOCK_GROUPS
[
0
],
frequency
:
1
},
{
id
:
1
,
frequency
:
2
,
lastUsed
:
PREV_TIME
},
{
...
MOCK_GROUPS
[
1
],
frequency
:
1
},
{
id
:
2
,
frequency
:
1
,
lastUsed
:
PREV_TIME
},
{
id
:
3
,
frequency
:
1
,
lastUsed
:
PREV_TIME
},
];
];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
1
]
);
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
{
id
:
3
}
);
});
});
it
(
'
sorts the array by most frequent
'
,
()
=>
{
it
(
'
sorts the array by most frequent
and lastUsed
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([
JSON
.
stringify
([
{
...
MOCK_GROUPS
[
1
],
frequency
:
2
},
{
id
:
3
,
frequency
:
2
,
lastUsed
:
CURRENT_TIME
},
{
...
MOCK_GROUPS
[
0
],
frequency
:
1
},
{
id
:
1
,
frequency
:
2
,
lastUsed
:
PREV_TIME
},
{
id
:
2
,
frequency
:
1
,
lastUsed
:
PREV_TIME
},
]),
]),
);
);
});
});
...
@@ -116,24 +125,24 @@ describe('Global Search Store Utils', () => {
...
@@ -116,24 +125,24 @@ describe('Global Search Store Utils', () => {
describe
(
'
with max entries
'
,
()
=>
{
describe
(
'
with max entries
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[
frequentItems
[
MOCK_LS_KEY
]
=
[
{
id
:
1
,
frequency
:
5
},
{
id
:
1
,
frequency
:
5
,
lastUsed
:
PREV_TIME
},
{
id
:
2
,
frequency
:
4
},
{
id
:
2
,
frequency
:
4
,
lastUsed
:
PREV_TIME
},
{
id
:
3
,
frequency
:
3
},
{
id
:
3
,
frequency
:
3
,
lastUsed
:
PREV_TIME
},
{
id
:
4
,
frequency
:
2
},
{
id
:
4
,
frequency
:
2
,
lastUsed
:
PREV_TIME
},
{
id
:
5
,
frequency
:
1
},
{
id
:
5
,
frequency
:
1
,
lastUsed
:
PREV_TIME
},
];
];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
{
id
:
6
});
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
{
id
:
6
});
});
});
it
(
'
removes the l
east frequent
'
,
()
=>
{
it
(
'
removes the l
ast item in the array
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([
JSON
.
stringify
([
{
id
:
1
,
frequency
:
5
},
{
id
:
1
,
frequency
:
5
,
lastUsed
:
PREV_TIME
},
{
id
:
2
,
frequency
:
4
},
{
id
:
2
,
frequency
:
4
,
lastUsed
:
PREV_TIME
},
{
id
:
3
,
frequency
:
3
},
{
id
:
3
,
frequency
:
3
,
lastUsed
:
PREV_TIME
},
{
id
:
4
,
frequency
:
2
},
{
id
:
4
,
frequency
:
2
,
lastUsed
:
PREV_TIME
},
{
id
:
6
,
frequency
:
1
},
{
id
:
6
,
frequency
:
1
,
lastUsed
:
CURRENT_TIME
},
]),
]),
);
);
});
});
...
@@ -160,7 +169,7 @@ describe('Global Search Store Utils', () => {
...
@@ -160,7 +169,7 @@ describe('Global Search Store Utils', () => {
it
(
'
parses out extra data for LS
'
,
()
=>
{
it
(
'
parses out extra data for LS
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
MOCK_LS_KEY
,
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
1
}]),
JSON
.
stringify
([{
...
MOCK_GROUPS
[
0
],
frequency
:
1
,
lastUsed
:
CURRENT_TIME
}]),
);
);
});
});
});
});
...
...
spec/frontend/search/topbar/components/group_filter_spec.js
View file @
cb98fe72
...
@@ -35,6 +35,9 @@ describe('GroupFilter', () => {
...
@@ -35,6 +35,9 @@ describe('GroupFilter', () => {
...
initialState
,
...
initialState
,
},
},
actions
:
actionSpies
,
actions
:
actionSpies
,
getters
:
{
frequentGroups
:
()
=>
[],
},
});
});
wrapper
=
shallowMount
(
GroupFilter
,
{
wrapper
=
shallowMount
(
GroupFilter
,
{
...
...
spec/frontend/search/topbar/components/project_filter_spec.js
View file @
cb98fe72
...
@@ -35,6 +35,9 @@ describe('ProjectFilter', () => {
...
@@ -35,6 +35,9 @@ describe('ProjectFilter', () => {
...
initialState
,
...
initialState
,
},
},
actions
:
actionSpies
,
actions
:
actionSpies
,
getters
:
{
frequentProjects
:
()
=>
[],
},
});
});
wrapper
=
shallowMount
(
ProjectFilter
,
{
wrapper
=
shallowMount
(
ProjectFilter
,
{
...
...
spec/frontend/search/topbar/components/searchable_dropdown_spec.js
View file @
cb98fe72
...
@@ -2,9 +2,9 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from
...
@@ -2,9 +2,9 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from
import
{
shallowMount
,
mount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
,
mount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
MOCK_GROUPS
,
MOCK_GROUP
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
{
MOCK_GROUPS
,
MOCK_GROUP
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
SearchableDropdownItem
from
'
~/search/topbar/components/searchable_dropdown_item.vue
'
;
import
{
ANY_OPTION
,
GROUP_DATA
}
from
'
~/search/topbar/constants
'
;
import
{
ANY_OPTION
,
GROUP_DATA
}
from
'
~/search/topbar/constants
'
;
Vue
.
use
(
Vuex
);
Vue
.
use
(
Vuex
);
...
@@ -29,13 +29,15 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -29,13 +29,15 @@ describe('Global Search Searchable Dropdown', () => {
},
},
});
});
wrapper
=
mountFn
(
SearchableDropdown
,
{
wrapper
=
extendedWrapper
(
store
,
mountFn
(
SearchableDropdown
,
{
propsData
:
{
store
,
...
defaultProps
,
propsData
:
{
...
props
,
...
defaultProps
,
},
...
props
,
});
},
}),
);
};
};
afterEach
(()
=>
{
afterEach
(()
=>
{
...
@@ -45,10 +47,11 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -45,10 +47,11 @@ describe('Global Search Searchable Dropdown', () => {
const
findGlDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
const
findGlDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
const
findGlDropdownSearch
=
()
=>
findGlDropdown
().
findComponent
(
GlSearchBoxByType
);
const
findGlDropdownSearch
=
()
=>
findGlDropdown
().
findComponent
(
GlSearchBoxByType
);
const
findDropdownText
=
()
=>
findGlDropdown
().
find
(
'
.dropdown-toggle-text
'
);
const
findDropdownText
=
()
=>
findGlDropdown
().
find
(
'
.dropdown-toggle-text
'
);
const
findSearchableDropdownItems
=
()
=>
const
findSearchableDropdownItems
=
()
=>
wrapper
.
findAllByTestId
(
'
searchable-items
'
);
findGlDropdown
().
findAllComponents
(
SearchableDropdownItem
);
const
findFrequentDropdownItems
=
()
=>
wrapper
.
findAllByTestId
(
'
frequent-items
'
);
const
findAnyDropdownItem
=
()
=>
findGlDropdown
().
findComponent
(
GlDropdownItem
);
const
findAnyDropdownItem
=
()
=>
findGlDropdown
().
findComponent
(
GlDropdownItem
);
const
findFirstGroupDropdownItem
=
()
=>
findSearchableDropdownItems
().
at
(
0
);
const
findFirstSearchableDropdownItem
=
()
=>
findSearchableDropdownItems
().
at
(
0
);
const
findFirstFrequentDropdownItem
=
()
=>
findFrequentDropdownItems
().
at
(
0
);
const
findLoader
=
()
=>
wrapper
.
findComponent
(
GlSkeletonLoader
);
const
findLoader
=
()
=>
wrapper
.
findComponent
(
GlSkeletonLoader
);
describe
(
'
template
'
,
()
=>
{
describe
(
'
template
'
,
()
=>
{
...
@@ -82,7 +85,7 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -82,7 +85,7 @@ describe('Global Search Searchable Dropdown', () => {
});
});
});
});
describe
(
'
findDropdown
Items
'
,
()
=>
{
describe
(
'
Searchable Dropdown
Items
'
,
()
=>
{
describe
(
'
when loading is false
'
,
()
=>
{
describe
(
'
when loading is false
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
({},
{
items
:
MOCK_GROUPS
});
createComponent
({},
{
items
:
MOCK_GROUPS
});
...
@@ -96,7 +99,7 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -96,7 +99,7 @@ describe('Global Search Searchable Dropdown', () => {
expect
(
findAnyDropdownItem
().
exists
()).
toBe
(
true
);
expect
(
findAnyDropdownItem
().
exists
()).
toBe
(
true
);
});
});
it
(
'
renders
SearchableDropdownI
tem for each item
'
,
()
=>
{
it
(
'
renders
searchable dropdown i
tem for each item
'
,
()
=>
{
expect
(
findSearchableDropdownItems
()).
toHaveLength
(
MOCK_GROUPS
.
length
);
expect
(
findSearchableDropdownItems
()).
toHaveLength
(
MOCK_GROUPS
.
length
);
});
});
});
});
...
@@ -114,12 +117,31 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -114,12 +117,31 @@ describe('Global Search Searchable Dropdown', () => {
expect
(
findAnyDropdownItem
().
exists
()).
toBe
(
true
);
expect
(
findAnyDropdownItem
().
exists
()).
toBe
(
true
);
});
});
it
(
'
does not render
SearchableDropdownItem
'
,
()
=>
{
it
(
'
does not render
searchable dropdown items
'
,
()
=>
{
expect
(
findSearchableDropdownItems
()).
toHaveLength
(
0
);
expect
(
findSearchableDropdownItems
()).
toHaveLength
(
0
);
});
});
});
});
});
});
describe
.
each
`
searchText | frequentItems | length
${
''
}
|
${[]}
|
${
0
}
${
''
}
|
${
MOCK_GROUPS
}
|
${
MOCK_GROUPS
.
length
}
${
'
test
'
}
|
${[]}
|
${
0
}
${
'
test
'
}
|
${
MOCK_GROUPS
}
|
${
0
}
`
(
'
Frequent Dropdown Items
'
,
({
searchText
,
frequentItems
,
length
})
=>
{
describe
(
`when search is
${
searchText
}
and frequentItems length is
${
frequentItems
.
length
}
`
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({},
{
frequentItems
});
wrapper
.
setData
({
searchText
});
});
it
(
`should
${
length
?
''
:
'
not
'
}
render frequent dropdown items`
,
()
=>
{
expect
(
findFrequentDropdownItems
()).
toHaveLength
(
length
);
});
});
});
describe
(
'
Dropdown Text
'
,
()
=>
{
describe
(
'
Dropdown Text
'
,
()
=>
{
describe
(
'
when selectedItem is any
'
,
()
=>
{
describe
(
'
when selectedItem is any
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
@@ -145,7 +167,7 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -145,7 +167,7 @@ describe('Global Search Searchable Dropdown', () => {
describe
(
'
actions
'
,
()
=>
{
describe
(
'
actions
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
({},
{
items
:
MOCK_GROUPS
});
createComponent
({},
{
items
:
MOCK_GROUPS
,
frequentItems
:
MOCK_GROUPS
});
});
});
it
(
'
clicking "Any" dropdown item $emits @change with ANY_OPTION
'
,
()
=>
{
it
(
'
clicking "Any" dropdown item $emits @change with ANY_OPTION
'
,
()
=>
{
...
@@ -154,8 +176,14 @@ describe('Global Search Searchable Dropdown', () => {
...
@@ -154,8 +176,14 @@ describe('Global Search Searchable Dropdown', () => {
expect
(
wrapper
.
emitted
(
'
change
'
)[
0
]).
toEqual
([
ANY_OPTION
]);
expect
(
wrapper
.
emitted
(
'
change
'
)[
0
]).
toEqual
([
ANY_OPTION
]);
});
});
it
(
'
on SearchableDropdownItem @change, the wrapper $emits change with the item
'
,
()
=>
{
it
(
'
on searchable item @change, the wrapper $emits change with the item
'
,
()
=>
{
findFirstGroupDropdownItem
().
vm
.
$emit
(
'
change
'
,
MOCK_GROUPS
[
0
]);
findFirstSearchableDropdownItem
().
vm
.
$emit
(
'
change
'
,
MOCK_GROUPS
[
0
]);
expect
(
wrapper
.
emitted
(
'
change
'
)[
0
]).
toEqual
([
MOCK_GROUPS
[
0
]]);
});
it
(
'
on frequent item @change, the wrapper $emits change with the item
'
,
()
=>
{
findFirstFrequentDropdownItem
().
vm
.
$emit
(
'
change
'
,
MOCK_GROUPS
[
0
]);
expect
(
wrapper
.
emitted
(
'
change
'
)[
0
]).
toEqual
([
MOCK_GROUPS
[
0
]]);
expect
(
wrapper
.
emitted
(
'
change
'
)[
0
]).
toEqual
([
MOCK_GROUPS
[
0
]]);
});
});
...
...
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