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
36004ac8
Commit
36004ac8
authored
Jun 30, 2021
by
Zack Cuddy
Committed by
Simon Knox
Jun 30, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Global Search - Store Recent Item Data
parent
e5711540
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
421 additions
and
15 deletions
+421
-15
app/assets/javascripts/search/store/actions.js
app/assets/javascripts/search/store/actions.js
+20
-0
app/assets/javascripts/search/store/constants.js
app/assets/javascripts/search/store/constants.js
+7
-0
app/assets/javascripts/search/store/mutation_types.js
app/assets/javascripts/search/store/mutation_types.js
+2
-0
app/assets/javascripts/search/store/mutations.js
app/assets/javascripts/search/store/mutations.js
+3
-0
app/assets/javascripts/search/store/state.js
app/assets/javascripts/search/store/state.js
+6
-0
app/assets/javascripts/search/store/utils.js
app/assets/javascripts/search/store/utils.js
+50
-0
app/assets/javascripts/search/topbar/components/group_filter.vue
...ets/javascripts/search/topbar/components/group_filter.vue
+9
-1
app/assets/javascripts/search/topbar/components/project_filter.vue
...s/javascripts/search/topbar/components/project_filter.vue
+9
-1
spec/frontend/search/mock_data.js
spec/frontend/search/mock_data.js
+2
-0
spec/frontend/search/store/actions_spec.js
spec/frontend/search/store/actions_spec.js
+85
-1
spec/frontend/search/store/mutations_spec.js
spec/frontend/search/store/mutations_spec.js
+9
-0
spec/frontend/search/store/utils_spec.js
spec/frontend/search/store/utils_spec.js
+147
-0
spec/frontend/search/topbar/components/group_filter_spec.js
spec/frontend/search/topbar/components/group_filter_spec.js
+43
-6
spec/frontend/search/topbar/components/project_filter_spec.js
.../frontend/search/topbar/components/project_filter_spec.js
+29
-6
No files found.
app/assets/javascripts/search/store/actions.js
View file @
36004ac8
...
@@ -2,7 +2,9 @@ import Api from '~/api';
...
@@ -2,7 +2,9 @@ import Api from '~/api';
import
createFlash
from
'
~/flash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
GROUPS_LOCAL_STORAGE_KEY
,
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
./constants
'
;
import
*
as
types
from
'
./mutation_types
'
;
import
*
as
types
from
'
./mutation_types
'
;
import
{
loadDataFromLS
,
setFrequentItemToLS
}
from
'
./utils
'
;
export
const
fetchGroups
=
({
commit
},
search
)
=>
{
export
const
fetchGroups
=
({
commit
},
search
)
=>
{
commit
(
types
.
REQUEST_GROUPS
);
commit
(
types
.
REQUEST_GROUPS
);
...
@@ -39,6 +41,24 @@ export const fetchProjects = ({ commit, state }, search) => {
...
@@ -39,6 +41,24 @@ export const fetchProjects = ({ commit, state }, search) => {
}
}
};
};
export
const
loadFrequentGroups
=
({
commit
})
=>
{
const
data
=
loadDataFromLS
(
GROUPS_LOCAL_STORAGE_KEY
);
commit
(
types
.
LOAD_FREQUENT_ITEMS
,
{
key
:
GROUPS_LOCAL_STORAGE_KEY
,
data
});
};
export
const
loadFrequentProjects
=
({
commit
})
=>
{
const
data
=
loadDataFromLS
(
PROJECTS_LOCAL_STORAGE_KEY
);
commit
(
types
.
LOAD_FREQUENT_ITEMS
,
{
key
:
PROJECTS_LOCAL_STORAGE_KEY
,
data
});
};
export
const
setFrequentGroup
=
({
state
},
item
)
=>
{
setFrequentItemToLS
(
GROUPS_LOCAL_STORAGE_KEY
,
state
.
frequentItems
,
item
);
};
export
const
setFrequentProject
=
({
state
},
item
)
=>
{
setFrequentItemToLS
(
PROJECTS_LOCAL_STORAGE_KEY
,
state
.
frequentItems
,
item
);
};
export
const
setQuery
=
({
commit
},
{
key
,
value
})
=>
{
export
const
setQuery
=
({
commit
},
{
key
,
value
})
=>
{
commit
(
types
.
SET_QUERY
,
{
key
,
value
});
commit
(
types
.
SET_QUERY
,
{
key
,
value
});
};
};
...
...
app/assets/javascripts/search/store/constants.js
0 → 100644
View file @
36004ac8
export
const
MAX_FREQUENT_ITEMS
=
5
;
export
const
MAX_FREQUENCY
=
5
;
export
const
GROUPS_LOCAL_STORAGE_KEY
=
'
global-search-frequent-groups
'
;
export
const
PROJECTS_LOCAL_STORAGE_KEY
=
'
global-search-frequent-projects
'
;
app/assets/javascripts/search/store/mutation_types.js
View file @
36004ac8
...
@@ -7,3 +7,5 @@ export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
...
@@ -7,3 +7,5 @@ export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export
const
RECEIVE_PROJECTS_ERROR
=
'
RECEIVE_PROJECTS_ERROR
'
;
export
const
RECEIVE_PROJECTS_ERROR
=
'
RECEIVE_PROJECTS_ERROR
'
;
export
const
SET_QUERY
=
'
SET_QUERY
'
;
export
const
SET_QUERY
=
'
SET_QUERY
'
;
export
const
LOAD_FREQUENT_ITEMS
=
'
LOAD_FREQUENT_ITEMS
'
;
app/assets/javascripts/search/store/mutations.js
View file @
36004ac8
...
@@ -26,4 +26,7 @@ export default {
...
@@ -26,4 +26,7 @@ export default {
[
types
.
SET_QUERY
](
state
,
{
key
,
value
})
{
[
types
.
SET_QUERY
](
state
,
{
key
,
value
})
{
state
.
query
[
key
]
=
value
;
state
.
query
[
key
]
=
value
;
},
},
[
types
.
LOAD_FREQUENT_ITEMS
](
state
,
{
key
,
data
})
{
state
.
frequentItems
[
key
]
=
data
;
},
};
};
app/assets/javascripts/search/store/state.js
View file @
36004ac8
import
{
GROUPS_LOCAL_STORAGE_KEY
,
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
./constants
'
;
const
createState
=
({
query
})
=>
({
const
createState
=
({
query
})
=>
({
query
,
query
,
groups
:
[],
groups
:
[],
fetchingGroups
:
false
,
fetchingGroups
:
false
,
projects
:
[],
projects
:
[],
fetchingProjects
:
false
,
fetchingProjects
:
false
,
frequentItems
:
{
[
GROUPS_LOCAL_STORAGE_KEY
]:
[],
[
PROJECTS_LOCAL_STORAGE_KEY
]:
[],
},
});
});
export
default
createState
;
export
default
createState
;
app/assets/javascripts/search/store/utils.js
0 → 100644
View file @
36004ac8
import
AccessorUtilities
from
'
../../lib/utils/accessor
'
;
import
{
MAX_FREQUENT_ITEMS
,
MAX_FREQUENCY
}
from
'
./constants
'
;
export
const
loadDataFromLS
=
(
key
)
=>
{
if
(
!
AccessorUtilities
.
isLocalStorageAccessSafe
())
{
return
[];
}
try
{
return
JSON
.
parse
(
localStorage
.
getItem
(
key
))
||
[];
}
catch
{
// The LS got in a bad state, let's wipe it
localStorage
.
removeItem
(
key
);
return
[];
}
};
export
const
setFrequentItemToLS
=
(
key
,
data
,
item
)
=>
{
if
(
!
AccessorUtilities
.
isLocalStorageAccessSafe
())
{
return
;
}
try
{
const
frequentItems
=
data
[
key
];
const
existingItemIndex
=
frequentItems
.
findIndex
((
i
)
=>
i
.
id
===
item
.
id
);
if
(
existingItemIndex
>=
0
)
{
// Up the frequency (Max 5)
const
currentFrequency
=
frequentItems
[
existingItemIndex
].
frequency
;
frequentItems
[
existingItemIndex
].
frequency
=
Math
.
min
(
currentFrequency
+
1
,
MAX_FREQUENCY
);
}
else
{
// Only store a max of 5 items
if
(
frequentItems
.
length
>=
MAX_FREQUENT_ITEMS
)
{
frequentItems
.
pop
();
}
frequentItems
.
push
({
id
:
item
.
id
,
frequency
:
1
});
}
// Sort by frequency
frequentItems
.
sort
((
a
,
b
)
=>
b
.
frequency
-
a
.
frequency
);
// Note we do not need to commit a mutation here as immediately after this we refresh the page to
// update the search results.
localStorage
.
setItem
(
key
,
JSON
.
stringify
(
frequentItems
));
}
catch
{
// The LS got in a bad state, let's wipe it
localStorage
.
removeItem
(
key
);
}
};
app/assets/javascripts/search/topbar/components/group_filter.vue
View file @
36004ac8
...
@@ -23,9 +23,17 @@ export default {
...
@@ -23,9 +23,17 @@ export default {
return
isEmpty
(
this
.
initialData
)
?
ANY_OPTION
:
this
.
initialData
;
return
isEmpty
(
this
.
initialData
)
?
ANY_OPTION
:
this
.
initialData
;
},
},
},
},
created
()
{
this
.
loadFrequentGroups
();
},
methods
:
{
methods
:
{
...
mapActions
([
'
fetchGroups
'
]),
...
mapActions
([
'
fetchGroups
'
,
'
setFrequentGroup
'
,
'
loadFrequentGroups
'
]),
handleGroupChange
(
group
)
{
handleGroupChange
(
group
)
{
// If group.id is null we are clearing the filter and don't need to store that in LS.
if
(
group
.
id
)
{
this
.
setFrequentGroup
(
group
);
}
visitUrl
(
visitUrl
(
setUrlParams
({
[
GROUP_DATA
.
queryParam
]:
group
.
id
,
[
PROJECT_DATA
.
queryParam
]:
null
}),
setUrlParams
({
[
GROUP_DATA
.
queryParam
]:
group
.
id
,
[
PROJECT_DATA
.
queryParam
]:
null
}),
);
);
...
...
app/assets/javascripts/search/topbar/components/project_filter.vue
View file @
36004ac8
...
@@ -22,9 +22,17 @@ export default {
...
@@ -22,9 +22,17 @@ export default {
return
this
.
initialData
?
this
.
initialData
:
ANY_OPTION
;
return
this
.
initialData
?
this
.
initialData
:
ANY_OPTION
;
},
},
},
},
created
()
{
this
.
loadFrequentProjects
();
},
methods
:
{
methods
:
{
...
mapActions
([
'
fetchProjects
'
]),
...
mapActions
([
'
fetchProjects
'
,
'
setFrequentProject
'
,
'
loadFrequentProjects
'
]),
handleProjectChange
(
project
)
{
handleProjectChange
(
project
)
{
// If project.id is null we are clearing the filter and don't need to store that in LS.
if
(
project
.
id
)
{
this
.
setFrequentProject
(
project
);
}
// This determines if we need to update the group filter or not
// This determines if we need to update the group filter or not
const
queryParams
=
{
const
queryParams
=
{
...(
project
.
namespace
?.
id
&&
{
[
GROUP_DATA
.
queryParam
]:
project
.
namespace
.
id
}),
...(
project
.
namespace
?.
id
&&
{
[
GROUP_DATA
.
queryParam
]:
project
.
namespace
.
id
}),
...
...
spec/frontend/search/mock_data.js
View file @
36004ac8
...
@@ -63,3 +63,5 @@ export const MOCK_SORT_OPTIONS = [
...
@@ -63,3 +63,5 @@ export const MOCK_SORT_OPTIONS = [
},
},
},
},
];
];
export
const
MOCK_LS_KEY
=
'
mock-ls-key
'
;
spec/frontend/search/store/actions_spec.js
View file @
36004ac8
...
@@ -5,9 +5,11 @@ import createFlash from '~/flash';
...
@@ -5,9 +5,11 @@ import createFlash from '~/flash';
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
*
as
urlUtils
from
'
~/lib/utils/url_utility
'
;
import
*
as
urlUtils
from
'
~/lib/utils/url_utility
'
;
import
*
as
actions
from
'
~/search/store/actions
'
;
import
*
as
actions
from
'
~/search/store/actions
'
;
import
{
GROUPS_LOCAL_STORAGE_KEY
,
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
~/search/store/constants
'
;
import
*
as
types
from
'
~/search/store/mutation_types
'
;
import
*
as
types
from
'
~/search/store/mutation_types
'
;
import
createState
from
'
~/search/store/state
'
;
import
createState
from
'
~/search/store/state
'
;
import
{
MOCK_QUERY
,
MOCK_GROUPS
,
MOCK_PROJECT
,
MOCK_PROJECTS
}
from
'
../mock_data
'
;
import
*
as
storeUtils
from
'
~/search/store/utils
'
;
import
{
MOCK_QUERY
,
MOCK_GROUPS
,
MOCK_PROJECT
,
MOCK_PROJECTS
,
MOCK_GROUP
}
from
'
../mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
...
@@ -141,4 +143,86 @@ describe('Global Search Store Actions', () => {
...
@@ -141,4 +143,86 @@ describe('Global Search Store Actions', () => {
});
});
});
});
});
});
describe
(
'
loadFrequentGroups
'
,
()
=>
{
beforeEach
(()
=>
{
storeUtils
.
loadDataFromLS
=
jest
.
fn
().
mockReturnValue
(
MOCK_GROUPS
);
});
it
(
`calls loadDataFromLS with
${
GROUPS_LOCAL_STORAGE_KEY
}
and LOAD_FREQUENT_ITEMS mutation`
,
async
()
=>
{
await
testAction
({
action
:
actions
.
loadFrequentGroups
,
state
,
expectedMutations
:
[
{
type
:
types
.
LOAD_FREQUENT_ITEMS
,
payload
:
{
key
:
GROUPS_LOCAL_STORAGE_KEY
,
data
:
MOCK_GROUPS
},
},
],
});
expect
(
storeUtils
.
loadDataFromLS
).
toHaveBeenCalledWith
(
GROUPS_LOCAL_STORAGE_KEY
);
});
});
describe
(
'
loadFrequentProjects
'
,
()
=>
{
beforeEach
(()
=>
{
storeUtils
.
loadDataFromLS
=
jest
.
fn
().
mockReturnValue
(
MOCK_PROJECTS
);
});
it
(
`calls loadDataFromLS with
${
PROJECTS_LOCAL_STORAGE_KEY
}
and LOAD_FREQUENT_ITEMS mutation`
,
async
()
=>
{
await
testAction
({
action
:
actions
.
loadFrequentProjects
,
state
,
expectedMutations
:
[
{
type
:
types
.
LOAD_FREQUENT_ITEMS
,
payload
:
{
key
:
PROJECTS_LOCAL_STORAGE_KEY
,
data
:
MOCK_PROJECTS
},
},
],
});
expect
(
storeUtils
.
loadDataFromLS
).
toHaveBeenCalledWith
(
PROJECTS_LOCAL_STORAGE_KEY
);
});
});
describe
(
'
setFrequentGroup
'
,
()
=>
{
beforeEach
(()
=>
{
storeUtils
.
setFrequentItemToLS
=
jest
.
fn
();
});
it
(
`calls setFrequentItemToLS with
${
GROUPS_LOCAL_STORAGE_KEY
}
and item data`
,
async
()
=>
{
await
testAction
({
action
:
actions
.
setFrequentGroup
,
payload
:
MOCK_GROUP
,
state
,
});
expect
(
storeUtils
.
setFrequentItemToLS
).
toHaveBeenCalledWith
(
GROUPS_LOCAL_STORAGE_KEY
,
state
.
frequentItems
,
MOCK_GROUP
,
);
});
});
describe
(
'
setFrequentProject
'
,
()
=>
{
beforeEach
(()
=>
{
storeUtils
.
setFrequentItemToLS
=
jest
.
fn
();
});
it
(
`calls setFrequentItemToLS with
${
PROJECTS_LOCAL_STORAGE_KEY
}
and item data`
,
async
()
=>
{
await
testAction
({
action
:
actions
.
setFrequentProject
,
payload
:
MOCK_PROJECT
,
state
,
});
expect
(
storeUtils
.
setFrequentItemToLS
).
toHaveBeenCalledWith
(
PROJECTS_LOCAL_STORAGE_KEY
,
state
.
frequentItems
,
MOCK_PROJECT
,
);
});
});
});
});
spec/frontend/search/store/mutations_spec.js
View file @
36004ac8
...
@@ -71,4 +71,13 @@ describe('Global Search Store Mutations', () => {
...
@@ -71,4 +71,13 @@ describe('Global Search Store Mutations', () => {
expect
(
state
.
query
[
payload
.
key
]).
toBe
(
payload
.
value
);
expect
(
state
.
query
[
payload
.
key
]).
toBe
(
payload
.
value
);
});
});
});
});
describe
(
'
LOAD_FREQUENT_ITEMS
'
,
()
=>
{
it
(
'
sets frequentItems[key] to data
'
,
()
=>
{
const
payload
=
{
key
:
'
test-key
'
,
data
:
[
1
,
2
,
3
]
};
mutations
[
types
.
LOAD_FREQUENT_ITEMS
](
state
,
payload
);
expect
(
state
.
frequentItems
[
payload
.
key
]).
toStrictEqual
(
payload
.
data
);
});
});
});
});
spec/frontend/search/store/utils_spec.js
0 → 100644
View file @
36004ac8
import
{
useLocalStorageSpy
}
from
'
helpers/local_storage_helper
'
;
import
{
MAX_FREQUENCY
}
from
'
~/search/store/constants
'
;
import
{
loadDataFromLS
,
setFrequentItemToLS
}
from
'
~/search/store/utils
'
;
import
{
MOCK_LS_KEY
,
MOCK_GROUPS
}
from
'
../mock_data
'
;
useLocalStorageSpy
();
jest
.
mock
(
'
~/lib/utils/accessor
'
,
()
=>
({
isLocalStorageAccessSafe
:
jest
.
fn
().
mockReturnValue
(
true
),
}));
describe
(
'
Global Search Store Utils
'
,
()
=>
{
afterEach
(()
=>
{
localStorage
.
clear
();
});
describe
(
'
loadDataFromLS
'
,
()
=>
{
let
res
;
describe
(
'
with valid data
'
,
()
=>
{
beforeEach
(()
=>
{
localStorage
.
setItem
(
MOCK_LS_KEY
,
JSON
.
stringify
(
MOCK_GROUPS
));
res
=
loadDataFromLS
(
MOCK_LS_KEY
);
});
it
(
'
returns parsed array
'
,
()
=>
{
expect
(
res
).
toStrictEqual
(
MOCK_GROUPS
);
});
});
describe
(
'
with invalid data
'
,
()
=>
{
beforeEach
(()
=>
{
localStorage
.
setItem
(
MOCK_LS_KEY
,
'
[}
'
);
res
=
loadDataFromLS
(
MOCK_LS_KEY
);
});
it
(
'
wipes local storage and returns an empty array
'
,
()
=>
{
expect
(
localStorage
.
removeItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
);
expect
(
res
).
toStrictEqual
([]);
});
});
});
describe
(
'
setFrequentItemToLS
'
,
()
=>
{
const
frequentItems
=
{};
describe
(
'
with existing data
'
,
()
=>
{
describe
(
`when frequency is less than
${
MAX_FREQUENCY
}
`
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
1
}];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
it
(
'
adds 1 to the frequency and calls localStorage.setItem
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
JSON
.
stringify
([{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
2
}]),
);
});
});
describe
(
`when frequency is equal to
${
MAX_FREQUENCY
}
`
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
MAX_FREQUENCY
}];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
it
(
`does not further increase frequency past
${
MAX_FREQUENCY
}
and calls localStorage.setItem`
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
JSON
.
stringify
([{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
MAX_FREQUENCY
}]),
);
});
});
});
describe
(
'
with no existing data
'
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
it
(
'
adds a new entry with frequency 1 and calls localStorage.setItem
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
JSON
.
stringify
([{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
1
}]),
);
});
});
describe
(
'
with multiple entries
'
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[
{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
1
},
{
id
:
MOCK_GROUPS
[
1
].
id
,
frequency
:
1
},
];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
1
]);
});
it
(
'
sorts the array by most frequent
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
JSON
.
stringify
([
{
id
:
MOCK_GROUPS
[
1
].
id
,
frequency
:
2
},
{
id
:
MOCK_GROUPS
[
0
].
id
,
frequency
:
1
},
]),
);
});
});
describe
(
'
with max entries
'
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
[
{
id
:
1
,
frequency
:
5
},
{
id
:
2
,
frequency
:
4
},
{
id
:
3
,
frequency
:
3
},
{
id
:
4
,
frequency
:
2
},
{
id
:
5
,
frequency
:
1
},
];
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
{
id
:
6
});
});
it
(
'
removes the least frequent
'
,
()
=>
{
expect
(
localStorage
.
setItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
,
JSON
.
stringify
([
{
id
:
1
,
frequency
:
5
},
{
id
:
2
,
frequency
:
4
},
{
id
:
3
,
frequency
:
3
},
{
id
:
4
,
frequency
:
2
},
{
id
:
6
,
frequency
:
1
},
]),
);
});
});
describe
(
'
with null data loaded in
'
,
()
=>
{
beforeEach
(()
=>
{
frequentItems
[
MOCK_LS_KEY
]
=
null
;
setFrequentItemToLS
(
MOCK_LS_KEY
,
frequentItems
,
MOCK_GROUPS
[
0
]);
});
it
(
'
wipes local storage
'
,
()
=>
{
expect
(
localStorage
.
removeItem
).
toHaveBeenCalledWith
(
MOCK_LS_KEY
);
});
});
});
});
spec/frontend/search/topbar/components/group_filter_spec.js
View file @
36004ac8
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
{
MOCK_GROUP
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
{
MOCK_GROUP
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
GROUPS_LOCAL_STORAGE_KEY
}
from
'
~/search/store/constants
'
;
import
GroupFilter
from
'
~/search/topbar/components/group_filter.vue
'
;
import
GroupFilter
from
'
~/search/topbar/components/group_filter.vue
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
~/search/topbar/constants
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
~/search/topbar/constants
'
;
const
localVue
=
createLocalVue
();
Vue
.
use
(
Vuex
);
localVue
.
use
(
Vuex
);
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
visitUrl
:
jest
.
fn
(),
visitUrl
:
jest
.
fn
(),
...
@@ -19,6 +20,8 @@ describe('GroupFilter', () => {
...
@@ -19,6 +20,8 @@ describe('GroupFilter', () => {
const
actionSpies
=
{
const
actionSpies
=
{
fetchGroups
:
jest
.
fn
(),
fetchGroups
:
jest
.
fn
(),
setFrequentGroup
:
jest
.
fn
(),
loadFrequentGroups
:
jest
.
fn
(),
};
};
const
defaultProps
=
{
const
defaultProps
=
{
...
@@ -35,7 +38,6 @@ describe('GroupFilter', () => {
...
@@ -35,7 +38,6 @@ describe('GroupFilter', () => {
});
});
wrapper
=
shallowMount
(
GroupFilter
,
{
wrapper
=
shallowMount
(
GroupFilter
,
{
localVue
,
store
,
store
,
propsData
:
{
propsData
:
{
...
defaultProps
,
...
defaultProps
,
...
@@ -77,14 +79,35 @@ describe('GroupFilter', () => {
...
@@ -77,14 +79,35 @@ describe('GroupFilter', () => {
});
});
});
});
describe
(
'
when @change is emitted
'
,
()
=>
{
describe
(
'
when @change is emitted with Any
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
findSearchableDropdown
().
vm
.
$emit
(
'
change
'
,
ANY_OPTION
);
});
it
(
'
calls setUrlParams with group null, project id null, and then calls visitUrl
'
,
()
=>
{
expect
(
setUrlParams
).
toHaveBeenCalledWith
({
[
GROUP_DATA
.
queryParam
]:
null
,
[
PROJECT_DATA
.
queryParam
]:
null
,
});
expect
(
visitUrl
).
toHaveBeenCalled
();
});
it
(
'
does not call setFrequentGroup
'
,
()
=>
{
expect
(
actionSpies
.
setFrequentGroup
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'
when @change is emitted with a group
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
();
createComponent
();
findSearchableDropdown
().
vm
.
$emit
(
'
change
'
,
MOCK_GROUP
);
findSearchableDropdown
().
vm
.
$emit
(
'
change
'
,
MOCK_GROUP
);
});
});
it
(
'
calls
calls setUrlParams with group id, project id null, and
visitUrl
'
,
()
=>
{
it
(
'
calls
setUrlParams with group id, project id null, and then calls
visitUrl
'
,
()
=>
{
expect
(
setUrlParams
).
toHaveBeenCalledWith
({
expect
(
setUrlParams
).
toHaveBeenCalledWith
({
[
GROUP_DATA
.
queryParam
]:
MOCK_GROUP
.
id
,
[
GROUP_DATA
.
queryParam
]:
MOCK_GROUP
.
id
,
[
PROJECT_DATA
.
queryParam
]:
null
,
[
PROJECT_DATA
.
queryParam
]:
null
,
...
@@ -92,6 +115,10 @@ describe('GroupFilter', () => {
...
@@ -92,6 +115,10 @@ describe('GroupFilter', () => {
expect
(
visitUrl
).
toHaveBeenCalled
();
expect
(
visitUrl
).
toHaveBeenCalled
();
});
});
it
(
`calls setFrequentGroup with the group and
${
GROUPS_LOCAL_STORAGE_KEY
}
`
,
()
=>
{
expect
(
actionSpies
.
setFrequentGroup
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
MOCK_GROUP
);
});
});
});
});
});
...
@@ -118,4 +145,14 @@ describe('GroupFilter', () => {
...
@@ -118,4 +145,14 @@ describe('GroupFilter', () => {
});
});
});
});
});
});
describe
(
'
onCreate
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
calls loadFrequentGroups
'
,
()
=>
{
expect
(
actionSpies
.
loadFrequentGroups
).
toHaveBeenCalledTimes
(
1
);
});
});
});
});
spec/frontend/search/topbar/components/project_filter_spec.js
View file @
36004ac8
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
{
MOCK_PROJECT
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
{
MOCK_PROJECT
,
MOCK_QUERY
}
from
'
jest/search/mock_data
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
visitUrl
,
setUrlParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
PROJECTS_LOCAL_STORAGE_KEY
}
from
'
~/search/store/constants
'
;
import
ProjectFilter
from
'
~/search/topbar/components/project_filter.vue
'
;
import
ProjectFilter
from
'
~/search/topbar/components/project_filter.vue
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
SearchableDropdown
from
'
~/search/topbar/components/searchable_dropdown.vue
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
~/search/topbar/constants
'
;
import
{
ANY_OPTION
,
GROUP_DATA
,
PROJECT_DATA
}
from
'
~/search/topbar/constants
'
;
const
localVue
=
createLocalVue
();
Vue
.
use
(
Vuex
);
localVue
.
use
(
Vuex
);
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
visitUrl
:
jest
.
fn
(),
visitUrl
:
jest
.
fn
(),
...
@@ -19,6 +20,8 @@ describe('ProjectFilter', () => {
...
@@ -19,6 +20,8 @@ describe('ProjectFilter', () => {
const
actionSpies
=
{
const
actionSpies
=
{
fetchProjects
:
jest
.
fn
(),
fetchProjects
:
jest
.
fn
(),
setFrequentProject
:
jest
.
fn
(),
loadFrequentProjects
:
jest
.
fn
(),
};
};
const
defaultProps
=
{
const
defaultProps
=
{
...
@@ -35,7 +38,6 @@ describe('ProjectFilter', () => {
...
@@ -35,7 +38,6 @@ describe('ProjectFilter', () => {
});
});
wrapper
=
shallowMount
(
ProjectFilter
,
{
wrapper
=
shallowMount
(
ProjectFilter
,
{
localVue
,
store
,
store
,
propsData
:
{
propsData
:
{
...
defaultProps
,
...
defaultProps
,
...
@@ -84,12 +86,16 @@ describe('ProjectFilter', () => {
...
@@ -84,12 +86,16 @@ describe('ProjectFilter', () => {
findSearchableDropdown
().
vm
.
$emit
(
'
change
'
,
ANY_OPTION
);
findSearchableDropdown
().
vm
.
$emit
(
'
change
'
,
ANY_OPTION
);
});
});
it
(
'
calls setUrlParams with
project id, not
group id, then calls visitUrl
'
,
()
=>
{
it
(
'
calls setUrlParams with
null, no
group id, then calls visitUrl
'
,
()
=>
{
expect
(
setUrlParams
).
toHaveBeenCalledWith
({
expect
(
setUrlParams
).
toHaveBeenCalledWith
({
[
PROJECT_DATA
.
queryParam
]:
ANY_OPTION
.
id
,
[
PROJECT_DATA
.
queryParam
]:
null
,
});
});
expect
(
visitUrl
).
toHaveBeenCalled
();
expect
(
visitUrl
).
toHaveBeenCalled
();
});
});
it
(
'
does not call setFrequentProject
'
,
()
=>
{
expect
(
actionSpies
.
setFrequentProject
).
not
.
toHaveBeenCalled
();
});
});
});
describe
(
'
with a Project
'
,
()
=>
{
describe
(
'
with a Project
'
,
()
=>
{
...
@@ -104,6 +110,13 @@ describe('ProjectFilter', () => {
...
@@ -104,6 +110,13 @@ describe('ProjectFilter', () => {
});
});
expect
(
visitUrl
).
toHaveBeenCalled
();
expect
(
visitUrl
).
toHaveBeenCalled
();
});
});
it
(
`calls setFrequentProject with the group and
${
PROJECTS_LOCAL_STORAGE_KEY
}
`
,
()
=>
{
expect
(
actionSpies
.
setFrequentProject
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
MOCK_PROJECT
,
);
});
});
});
});
});
});
});
...
@@ -131,4 +144,14 @@ describe('ProjectFilter', () => {
...
@@ -131,4 +144,14 @@ describe('ProjectFilter', () => {
});
});
});
});
});
});
describe
(
'
onCreate
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
calls loadFrequentProjects
'
,
()
=>
{
expect
(
actionSpies
.
loadFrequentProjects
).
toHaveBeenCalledTimes
(
1
);
});
});
});
});
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