Commit 133cedea authored by David Pisek's avatar David Pisek Committed by Kushal Pandya

Add projectSelector module to security dashboard

This commit adds a new vuex store module to the security dashboard.

It is largely based off an existing store module
(ee/app/assets/javascripts/vue_shared/dashboards/store) but includes
some changes that aim to make it more generic.
parent f3ddb255
import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { __, s__, sprintf } from '~/locale';
import * as types from './mutation_types';
const API_MINIMUM_QUERY_LENGTH = 3;
export const toggleSelectedProject = ({ commit, state }, project) => {
const isProject = ({ id }) => id === project.id;
if (state.selectedProjects.some(isProject)) {
commit(types.DESELECT_PROJECT, project);
} else {
commit(types.SELECT_PROJECT, project);
}
};
export const clearSearchResults = ({ commit }) => {
commit(types.CLEAR_SEARCH_RESULTS);
};
export const setSearchQuery = ({ commit }, query) => {
commit(types.SET_SEARCH_QUERY, query);
};
export const setProjectEndpoints = ({ commit }, endpoints) => {
commit(types.SET_PROJECT_ENDPOINTS, endpoints);
};
export const addProjects = ({ state, dispatch }) => {
dispatch('requestAddProjects');
return axios
.post(state.projectEndpoints.add, {
project_ids: state.selectedProjects.map(p => p.id),
})
.then(response => dispatch('receiveAddProjectsSuccess', response.data))
.catch(() => dispatch('receiveAddProjectsError'));
};
export const requestAddProjects = ({ commit }) => {
commit(types.REQUEST_ADD_PROJECTS);
};
export const receiveAddProjectsSuccess = ({ commit, dispatch, state }, data) => {
const { added, invalid } = data;
commit(types.RECEIVE_ADD_PROJECTS_SUCCESS);
if (invalid.length) {
const [firstProject, secondProject, ...rest] = state.selectedProjects
.filter(project => invalid.includes(project.id))
.map(project => project.name);
const translationValues = {
firstProject,
secondProject,
rest: rest.join(', '),
};
let invalidProjects;
if (rest.length > 0) {
invalidProjects = sprintf(
s__('SecurityDashboard|%{firstProject}, %{secondProject}, and %{rest}'),
translationValues,
);
} else if (secondProject) {
invalidProjects = sprintf(
s__('SecurityDashboard|%{firstProject} and %{secondProject}'),
translationValues,
);
} else {
invalidProjects = firstProject;
}
createFlash(
sprintf(s__('SecurityDashboard|Unable to add %{invalidProjects}'), {
invalidProjects,
}),
);
}
if (added.length) {
dispatch('fetchProjects');
}
};
export const receiveAddProjectsError = ({ commit }) => {
commit(types.RECEIVE_ADD_PROJECTS_ERROR);
createFlash(__('Something went wrong, unable to add projects to dashboard'));
};
export const fetchProjects = ({ state, dispatch }) => {
dispatch('requestProjects');
return axios
.get(state.projectEndpoints.list)
.then(({ data }) => {
dispatch('receiveProjectsSuccess', data);
})
.catch(() => dispatch('receiveProjectsError'));
};
export const requestProjects = ({ commit }) => {
commit(types.REQUEST_PROJECTS);
};
export const receiveProjectsSuccess = ({ commit }, { projects }) => {
commit(types.RECEIVE_PROJECTS_SUCCESS, projects);
};
export const receiveProjectsError = ({ commit }) => {
commit(types.RECEIVE_PROJECTS_ERROR);
createFlash(__('Something went wrong, unable to get projects'));
};
export const removeProject = ({ dispatch }, removePath) => {
dispatch('requestRemoveProject');
return axios
.delete(removePath)
.then(() => {
dispatch('receiveRemoveProjectSuccess');
})
.catch(() => dispatch('receiveRemoveProjectError'));
};
export const requestRemoveProject = ({ commit }) => {
commit(types.REQUEST_REMOVE_PROJECT);
};
export const receiveRemoveProjectSuccess = ({ commit, dispatch }) => {
commit(types.RECEIVE_REMOVE_PROJECT_SUCCESS);
dispatch('fetchProjects');
};
export const receiveRemoveProjectError = ({ commit }) => {
commit(types.RECEIVE_REMOVE_PROJECT_ERROR);
createFlash(__('Something went wrong, unable to remove project'));
};
export const fetchSearchResults = ({ state, dispatch }) => {
const { searchQuery } = state;
dispatch('requestSearchResults');
if (!searchQuery || searchQuery.length < API_MINIMUM_QUERY_LENGTH) {
return dispatch('setMinimumQueryMessage');
}
return Api.projects(searchQuery, {})
.then(results => dispatch('receiveSearchResultsSuccess', results))
.catch(() => dispatch('receiveSearchResultsError'));
};
export const requestSearchResults = ({ commit }) => {
commit(types.REQUEST_SEARCH_RESULTS);
};
export const receiveSearchResultsSuccess = ({ commit }, results) => {
commit(types.RECEIVE_SEARCH_RESULTS_SUCCESS, results);
};
export const receiveSearchResultsError = ({ commit }) => {
commit(types.RECEIVE_SEARCH_RESULTS_ERROR);
};
export const setMinimumQueryMessage = ({ commit }) => {
commit(types.SET_MINIMUM_QUERY_MESSAGE);
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import state from './state';
import mutations from './mutations';
import * as actions from './actions';
export default () => ({
namespaced: true,
state,
mutations,
actions,
});
export const SET_PROJECT_ENDPOINTS = 'SET_PROJECT_ENDPOINTS';
export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
export const SELECT_PROJECT = 'SELECT_PROJECT';
export const DESELECT_PROJECT = 'DESELECT_PROJECT';
export const REQUEST_ADD_PROJECTS = 'REQUEST_ADD_PROJECTS';
export const RECEIVE_ADD_PROJECTS_SUCCESS = 'RECEIVE_ADD_PROJECTS_SUCCESS';
export const RECEIVE_ADD_PROJECTS_ERROR = 'RECEIVE_ADD_PROJECTS_ERROR';
export const REQUEST_REMOVE_PROJECT = 'REQUEST_REMOVE_PROJECT';
export const RECEIVE_REMOVE_PROJECT_SUCCESS = 'RECEIVE_REMOVE_PROJECT_SUCCESS';
export const RECEIVE_REMOVE_PROJECT_ERROR = 'RECEIVE_REMOVE_PROJECT_ERROR';
export const REQUEST_PROJECTS = 'REQUEST_PROJECTS';
export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
export const CLEAR_SEARCH_RESULTS = 'CLEAR_SEARCH_RESULTS';
export const REQUEST_SEARCH_RESULTS = 'REQUEST_SEARCH_RESULTS';
export const RECEIVE_SEARCH_RESULTS_SUCCESS = 'RECEIVE_SEARCH_RESULTS_SUCCESS';
export const RECEIVE_SEARCH_RESULTS_ERROR = 'RECEIVE_SEARCH_RESULTS_ERROR';
export const SET_MINIMUM_QUERY_MESSAGE = 'SET_MINIMUM_QUERY_MESSAGE';
import * as types from './mutation_types';
export default {
[types.SET_PROJECT_ENDPOINTS](state, endpoints) {
state.projectEndpoints.add = endpoints.add;
state.projectEndpoints.list = endpoints.list;
},
[types.SET_SEARCH_QUERY](state, query) {
state.searchQuery = query;
},
[types.SELECT_PROJECT](state, project) {
if (!state.selectedProjects.some(p => p.id === project.id)) {
state.selectedProjects.push(project);
}
},
[types.DESELECT_PROJECT](state, project) {
state.selectedProjects = state.selectedProjects.filter(p => p.id !== project.id);
},
[types.REQUEST_ADD_PROJECTS](state) {
state.isAddingProjects = true;
},
[types.RECEIVE_ADD_PROJECTS_SUCCESS](state) {
state.isAddingProjects = false;
},
[types.RECEIVE_ADD_PROJECTS_ERROR](state) {
state.isAddingProjects = false;
},
[types.REQUEST_REMOVE_PROJECT](state) {
state.isRemovingProject = true;
},
[types.RECEIVE_REMOVE_PROJECT_SUCCESS](state) {
state.isRemovingProject = false;
},
[types.RECEIVE_REMOVE_PROJECT_ERROR](state) {
state.isRemovingProject = false;
},
[types.REQUEST_PROJECTS](state) {
state.isLoadingProjects = true;
},
[types.RECEIVE_PROJECTS_SUCCESS](state, projects) {
state.projects = projects;
state.isLoadingProjects = false;
},
[types.RECEIVE_PROJECTS_ERROR](state) {
state.projects = [];
state.isLoadingProjects = false;
},
[types.CLEAR_SEARCH_RESULTS](state) {
state.projectSearchResults = [];
state.selectedProjects = [];
},
[types.REQUEST_SEARCH_RESULTS](state) {
state.messages.minimumQuery = false;
state.searchCount += 1;
},
[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, results) {
state.projectSearchResults = results;
state.messages.noResults = state.projectSearchResults.length === 0;
state.messages.searchError = false;
state.messages.minimumQuery = false;
state.searchCount = Math.max(0, state.searchCount - 1);
},
[types.RECEIVE_SEARCH_RESULTS_ERROR](state) {
state.projectSearchResults = [];
state.messages.noResults = false;
state.messages.searchError = true;
state.messages.minimumQuery = false;
state.searchCount = Math.max(0, state.searchCount - 1);
},
[types.SET_MINIMUM_QUERY_MESSAGE](state) {
state.projectSearchResults = [];
state.messages.noResults = false;
state.messages.searchError = false;
state.messages.minimumQuery = true;
state.searchCount = Math.max(0, state.searchCount - 1);
},
};
export default () => ({
inputValue: '',
isLoadingProjects: false,
isAddingProjects: false,
isRemovingProject: false,
projectEndpoints: {
list: null,
add: null,
},
searchQuery: '',
projects: [],
projectSearchResults: [],
selectedProjects: [],
messages: {
noResults: false,
searchError: false,
minimumQuery: false,
},
searchCount: 0,
});
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
import createFlash from '~/flash';
import createState from 'ee/security_dashboard/store/modules/project_selector/state';
import * as types from 'ee/security_dashboard/store/modules/project_selector/mutation_types';
import * as actions from 'ee/security_dashboard/store/modules/project_selector/actions';
jest.mock('~/api');
jest.mock('~/flash');
describe('projectSelector actions', () => {
const getMockProjects = n => [...Array(n).keys()].map(i => ({ id: i, name: `project-${i}` }));
const mockAddEndpoint = 'mock-add_endpoint';
const mockListEndpoint = 'mock-list_endpoint';
const mockResponse = { data: 'mock-data' };
let mockAxios;
let mockDispatchContext;
let state;
beforeEach(() => {
mockAxios = new MockAdapter(axios);
mockDispatchContext = { dispatch: () => {}, commit: () => {}, state };
state = createState();
});
afterEach(() => {
jest.clearAllMocks();
mockAxios.restore();
});
describe('toggleSelectedProject', () => {
it('adds a project to selectedProjects if it does not already exist in the list', done => {
const payload = getMockProjects(1);
testAction(
actions.toggleSelectedProject,
payload,
state,
[
{
type: types.SELECT_PROJECT,
payload,
},
],
[],
done,
);
});
it('removes a project from selectedProjects if it already exist in the list', () => {
const payload = getMockProjects(1)[0];
state.selectedProjects = getMockProjects(1);
return testAction(
actions.toggleSelectedProject,
payload,
state,
[
{
type: types.DESELECT_PROJECT,
payload,
},
],
[],
);
});
});
describe('addProjects', () => {
it('posts selected project ids to project add endpoint', () => {
state.projectEndpoints.add = mockAddEndpoint;
mockAxios.onPost(mockAddEndpoint).replyOnce(200, mockResponse);
return testAction(
actions.addProjects,
null,
state,
[],
[
{
type: 'requestAddProjects',
},
{
type: 'receiveAddProjectsSuccess',
payload: mockResponse,
},
],
);
});
it('calls addProjects error handler on error', () => {
mockAxios.onPost(mockAddEndpoint).replyOnce(500);
return testAction(
actions.addProjects,
null,
state,
[],
[{ type: 'requestAddProjects' }, { type: 'receiveAddProjectsError' }],
);
});
});
describe('requestAddProjects', () => {
it('commits the REQUEST_ADD_PROJECTS mutation', () =>
testAction(
actions.requestAddProjects,
null,
state,
[
{
type: types.REQUEST_ADD_PROJECTS,
},
],
[],
));
});
describe('receiveAddProjectsSuccess', () => {
beforeEach(() => {
state.selectedProjects = getMockProjects(3);
});
it('fetches projects when new projects are added to the dashboard', () => {
const addedProject = state.selectedProjects[0];
const payload = {
added: [addedProject.id],
invalid: [],
duplicate: [],
};
return testAction(
actions.receiveAddProjectsSuccess,
payload,
state,
[{ type: types.RECEIVE_ADD_PROJECTS_SUCCESS }],
[
{
type: 'fetchProjects',
},
],
);
});
it('displays an error when user tries to add one invalid project to dashboard', () => {
const invalidProject = state.selectedProjects[0];
const payload = {
added: [],
invalid: [invalidProject.id],
};
return testAction(
actions.receiveAddProjectsSuccess,
payload,
state,
[{ type: types.RECEIVE_ADD_PROJECTS_SUCCESS }],
[],
).then(() => {
expect(createFlash).toHaveBeenCalledWith(`Unable to add ${invalidProject.name}`);
});
});
it('displays an error when user tries to add two invalid projects to dashboard', () => {
const invalidProject1 = state.selectedProjects[0];
const invalidProject2 = state.selectedProjects[1];
const payload = {
added: [],
invalid: [invalidProject1.id, invalidProject2.id],
};
return testAction(
actions.receiveAddProjectsSuccess,
payload,
state,
[{ type: types.RECEIVE_ADD_PROJECTS_SUCCESS }],
[],
).then(() => {
expect(createFlash).toHaveBeenCalledWith(
`Unable to add ${invalidProject1.name} and ${invalidProject2.name}`,
);
});
});
it('displays an error when user tries to add more than two invalid projects to dashboard', () => {
const invalidProject1 = state.selectedProjects[0];
const invalidProject2 = state.selectedProjects[1];
const invalidProject3 = state.selectedProjects[2];
const payload = {
added: [],
invalid: [invalidProject1.id, invalidProject2.id, invalidProject3.id],
};
return testAction(
actions.receiveAddProjectsSuccess,
payload,
state,
[{ type: types.RECEIVE_ADD_PROJECTS_SUCCESS }],
[],
).then(() => {
expect(createFlash).toHaveBeenCalledWith(
`Unable to add ${invalidProject1.name}, ${invalidProject2.name}, and ${invalidProject3.name}`,
);
});
});
});
describe('receiveAddProjectsError', () => {
it('commits RECEIVE_ADD_PROJECTS_ERROR', () =>
testAction(
actions.receiveAddProjectsError,
null,
state,
[
{
type: types.RECEIVE_ADD_PROJECTS_ERROR,
},
],
[],
));
it('shows error message', () => {
actions.receiveAddProjectsError(mockDispatchContext);
expect(createFlash).toHaveBeenCalledTimes(1);
expect(createFlash).toHaveBeenCalledWith(
'Something went wrong, unable to add projects to dashboard',
);
});
});
describe('clearSearchResults', () => {
it('clears all project search results', () =>
testAction(
actions.clearSearchResults,
null,
state,
[
{
type: types.CLEAR_SEARCH_RESULTS,
},
],
[],
));
});
describe('fetchProjects', () => {
it('calls project list endpoint', () => {
state.projectEndpoints.list = mockListEndpoint;
mockAxios.onGet(mockListEndpoint).replyOnce(200, mockResponse);
return testAction(
actions.fetchProjects,
null,
state,
[],
[{ type: 'requestProjects' }, { type: 'receiveProjectsSuccess', payload: mockResponse }],
);
});
it('handles response errors', () => {
state.projectEndpoints.list = mockListEndpoint;
mockAxios.onGet(mockListEndpoint).replyOnce(500);
return testAction(
actions.fetchProjects,
null,
state,
[],
[{ type: 'requestProjects' }, { type: 'receiveProjectsError' }],
);
});
});
describe('requestProjects', () => {
it('toggles project loading state', () =>
testAction(actions.requestProjects, null, state, [{ type: types.REQUEST_PROJECTS }], []));
});
describe('receiveProjectsSuccess', () => {
it('sets projects from data on success', () => {
const payload = {
projects: [{ id: 0, name: 'mock-name1' }],
};
return testAction(
actions.receiveProjectsSuccess,
payload,
state,
[
{
type: types.RECEIVE_PROJECTS_SUCCESS,
payload: payload.projects,
},
],
[],
);
});
});
describe('receiveProjectsError', () => {
it('clears projects and alerts user of error', () =>
testAction(
actions.receiveProjectsError,
null,
state,
[
{
type: types.RECEIVE_PROJECTS_ERROR,
},
],
[],
).then(() => {
expect(createFlash).toHaveBeenCalledWith('Something went wrong, unable to get projects');
}));
});
describe('removeProject', () => {
const mockRemovePath = 'mock-removePath';
it('calls project removal path and fetches projects on success', () => {
mockAxios.onDelete(mockRemovePath).replyOnce(200);
return testAction(
actions.removeProject,
mockRemovePath,
null,
[],
[{ type: 'requestRemoveProject' }, { type: 'receiveRemoveProjectSuccess' }],
);
});
it('passes off handling of project removal errors', () => {
mockAxios.onDelete(mockRemovePath).replyOnce(500);
return testAction(
actions.removeProject,
mockRemovePath,
null,
[],
[{ type: 'requestRemoveProject' }, { type: 'receiveRemoveProjectError' }],
);
});
});
describe('requestRemoveProject', () => {
it('commits REQUEST_REMOVE_PROJECT mutation', () =>
testAction(
actions.requestRemoveProject,
null,
state,
[
{
type: types.REQUEST_REMOVE_PROJECT,
},
],
[],
));
});
describe('receiveRemoveProjectSuccess', () => {
it('commits RECEIVE_REMOVE_PROJECT_SUCCESS mutation and dispatches fetchProjects', () =>
testAction(
actions.receiveRemoveProjectSuccess,
null,
state,
[
{
type: types.RECEIVE_REMOVE_PROJECT_SUCCESS,
},
],
[{ type: 'fetchProjects' }],
));
});
describe('receiveRemoveProjectError', () => {
it('commits REQUEST_REMOVE_PROJECT mutation', () =>
testAction(
actions.receiveRemoveProjectError,
null,
state,
[
{
type: types.RECEIVE_REMOVE_PROJECT_ERROR,
},
],
[],
));
it('displays project removal error', () => {
actions.receiveRemoveProjectError(mockDispatchContext);
expect(createFlash).toHaveBeenCalledTimes(1);
expect(createFlash).toHaveBeenCalledWith('Something went wrong, unable to remove project');
});
});
describe('fetchSearchResults', () => {
it.each([null, undefined, false, NaN, 0, ''])(
'dispatches setMinimumQueryMessage if the search query is falsy',
searchQuery => {
state.searchQuery = searchQuery;
return testAction(
actions.fetchSearchResults,
null,
state,
[],
[
{
type: 'requestSearchResults',
},
{
type: 'setMinimumQueryMessage',
},
],
);
},
);
it.each(['a', 'aa'])(
'dispatches setMinimumQueryMessage if the search query was not long enough',
shortSearchQuery => {
state.searchQuery = shortSearchQuery;
return testAction(
actions.fetchSearchResults,
null,
state,
[],
[
{
type: 'requestSearchResults',
},
{
type: 'setMinimumQueryMessage',
},
],
);
},
);
it('dispatches the correct actions when the query is valid', () => {
const mockProjects = [{ id: 0, name: 'mock-name1' }];
Api.projects.mockResolvedValueOnce(mockProjects);
state.searchQuery = 'mock-query';
return testAction(
actions.fetchSearchResults,
null,
state,
[],
[
{
type: 'requestSearchResults',
},
{
type: 'receiveSearchResultsSuccess',
payload: mockProjects,
},
],
);
});
});
describe('requestSearchResults', () => {
it('commits the REQUEST_SEARCH_RESULTS mutation', () =>
testAction(
actions.requestSearchResults,
null,
state,
[
{
type: types.REQUEST_SEARCH_RESULTS,
},
],
[],
));
});
describe('receiveSearchResultsSuccess', () => {
it('commits the RECEIVE_SEARCH_RESULTS_SUCCESS mutation', () => {
const mockProjects = [{ id: 0, name: 'mock-project1' }];
return testAction(
actions.receiveSearchResultsSuccess,
mockProjects,
state,
[
{
type: types.RECEIVE_SEARCH_RESULTS_SUCCESS,
payload: mockProjects,
},
],
[],
);
});
});
describe('receiveSearchResultsError', () => {
it('commits the RECEIVE_SEARCH_RESULTS_ERROR mutation', () =>
testAction(
actions.receiveSearchResultsError,
['error'],
state,
[
{
type: types.RECEIVE_SEARCH_RESULTS_ERROR,
},
],
[],
));
});
describe('setProjectEndpoints', () => {
it('commits project list and add endpoints', () => {
const payload = {
add: 'add',
list: 'list',
};
return testAction(
actions.setProjectEndpoints,
payload,
state,
[
{
type: types.SET_PROJECT_ENDPOINTS,
payload,
},
],
[],
);
});
});
describe('setMinimumQueryMessage', () => {
it('commits the SET_MINIMUM_QUERY_MESSAGE mutation', () =>
testAction(
actions.setMinimumQueryMessage,
null,
state,
[
{
type: types.SET_MINIMUM_QUERY_MESSAGE,
},
],
[],
));
});
});
import createState from 'ee/security_dashboard/store/modules/project_selector/state';
import mutations from 'ee/security_dashboard/store/modules/project_selector/mutations';
import * as types from 'ee/security_dashboard/store/modules/project_selector/mutation_types';
describe('projectsSelector mutations', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('SET_PROJECT_ENDPOINTS', () => {
it('sets "projectEndpoints.list" and "projectEndpoints.add"', () => {
const payload = { list: 'list', add: 'add' };
state.projectEndpoints = {};
mutations[types.SET_PROJECT_ENDPOINTS](state, payload);
expect(state.projectEndpoints.list).toBe(payload.list);
expect(state.projectEndpoints.add).toBe(payload.add);
});
});
describe('SET_SEARCH_QUERY', () => {
it('sets "searchQuery" to be the given payload', () => {
const payload = 'searchQuery';
state.searchQuery = '';
mutations[types.SET_SEARCH_QUERY](state, payload);
expect(state.searchQuery).toBe(payload);
});
});
describe('SELECT_PROJECT', () => {
it('adds the given project to "selectedProjects"', () => {
const payload = {};
state.selectedProjects = [];
mutations[types.SELECT_PROJECT](state, payload);
expect(state.selectedProjects[0]).toBe(payload);
});
it('prevents projects from being added to "selectedProjects" twice', () => {
const payload1 = { id: 1 };
const payload2 = { id: 2 };
mutations[types.SELECT_PROJECT](state, payload1);
mutations[types.SELECT_PROJECT](state, payload1);
expect(state.selectedProjects).toHaveLength(1);
mutations[types.SELECT_PROJECT](state, payload2);
expect(state.selectedProjects).toHaveLength(2);
});
});
describe('DESELECT_PROJECT', () => {
it('removes the project with the given id from "selectedProjects"', () => {
state.selectedProjects = [{ id: 1 }, { id: 2 }];
const payload = { id: 1 };
mutations[types.DESELECT_PROJECT](state, payload);
expect(state.selectedProjects).toHaveLength(1);
expect(state.selectedProjects[0].id).toBe(2);
});
});
describe('REQUEST_ADD_PROJECTS', () => {
it('sets "isAddingProjects" to be true', () => {
state.isAddingProjects = false;
mutations[types.REQUEST_ADD_PROJECTS](state);
expect(state.isAddingProjects).toBe(true);
});
});
describe('RECEIVE_ADD_PROJECTS_SUCCESS', () => {
it('sets "isAddingProjects" to be true', () => {
state.isAddingProjects = true;
mutations[types.RECEIVE_ADD_PROJECTS_SUCCESS](state);
expect(state.isAddingProjects).toBe(false);
});
});
describe('RECEIVE_ADD_PROJECTS_ERROR', () => {
it('sets "isAddingProjects" to be true', () => {
state.isAddingProjects = true;
mutations[types.RECEIVE_ADD_PROJECTS_ERROR](state);
expect(state.isAddingProjects).toBe(false);
});
});
describe('REQUEST_REMOVE_PROJECT', () => {
it('sets "isRemovingProjects" to be true', () => {
state.isRemovingProject = false;
mutations[types.REQUEST_REMOVE_PROJECT](state);
expect(state.isRemovingProject).toBe(true);
});
});
describe('RECEIVE_REMOVE_PROJECT_SUCCESS', () => {
it('sets "isRemovingProjects" to be true', () => {
state.isRemovingProject = true;
mutations[types.RECEIVE_REMOVE_PROJECT_SUCCESS](state);
expect(state.isRemovingProject).toBe(false);
});
});
describe('RECEIVE_REMOVE_PROJECT_ERROR', () => {
it('sets "isRemovingProjects" to be true', () => {
state.isRemovingProject = true;
mutations[types.RECEIVE_REMOVE_PROJECT_ERROR](state);
expect(state.isRemovingProject).toBe(false);
});
});
describe('REQUEST_PROJECTS', () => {
it('sets "isLoadingProjects" to be true', () => {
state.isLoadingProjects = false;
mutations[types.REQUEST_PROJECTS](state);
expect(state.isLoadingProjects).toBe(true);
});
});
describe('RECEIVE_PROJECTS_SUCCESS', () => {
it('sets "projects" to be the payload', () => {
const payload = [];
state.projects = [];
mutations[types.RECEIVE_PROJECTS_SUCCESS](state, payload);
expect(state.projects).toBe(payload);
});
it('sets "isLoadingProjects" to be false', () => {
state.isLoadingProjects = true;
mutations[types.RECEIVE_PROJECTS_SUCCESS](state, []);
expect(state.isLoadingProjects).toBe(false);
});
});
describe('RECEIVE_PROJECTS_ERROR', () => {
it('sets "projects" to be an empty array', () => {
state.projects = [];
mutations[types.RECEIVE_PROJECTS_ERROR](state);
expect(state.projects).toEqual([]);
});
it('sets "isLoadingProjects" to be false', () => {
state.isLoadingProjects = true;
mutations[types.RECEIVE_PROJECTS_ERROR](state);
expect(state.isLoadingProjects).toBe(false);
});
});
describe('CLEAR_SEARCH_RESULTS', () => {
it('sets "projectSearchResults" to be an empty array', () => {
state.projectSearchResults = [''];
mutations[types.CLEAR_SEARCH_RESULTS](state);
expect(state.projectSearchResults).toHaveLength(0);
});
it('sets "selectedProjects" to be an empty array', () => {
state.selectedProjects = [''];
mutations[types.CLEAR_SEARCH_RESULTS](state);
expect(state.selectedProjects).toHaveLength(0);
});
});
describe('REQUEST_SEARCH_RESULTS', () => {
it('sets "messages.minimumQuery" to be false', () => {
state.messages.minimumQuery = true;
mutations[types.REQUEST_SEARCH_RESULTS](state);
expect(state.messages.minimumQuery).toBe(false);
});
it('increments "searchCount" by one', () => {
state.searchCount = 0;
mutations[types.REQUEST_SEARCH_RESULTS](state);
expect(state.searchCount).toBe(1);
});
});
describe('RECEIVE_SEARCH_RESULTS_SUCCESS', () => {
it('sets "projectSearchResults" to be the payload', () => {
const payload = [];
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, payload);
expect(state.projectSearchResults).toBe(payload);
});
it('sets "messages.noResults" to be false if the payload is not empty', () => {
state.messages.noResults = true;
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, ['']);
expect(state.messages.noResults).toBe(false);
});
it('sets "messages.searchError" to be false', () => {
state.messages.searchError = true;
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, ['']);
expect(state.messages.searchError).toBe(false);
});
it('sets "messages.minimumQuery" to be false', () => {
state.messages.minimumQuery = true;
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, ['']);
expect(state.messages.minimumQuery).toBe(false);
});
it('decrements "searchCount" by one', () => {
state.searchCount = 1;
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, ['']);
expect(state.searchCount).toBe(0);
});
it('does not decrement "searchCount" into negative', () => {
state.searchCount = 0;
mutations[types.RECEIVE_SEARCH_RESULTS_SUCCESS](state, ['']);
expect(state.searchCount).toBe(0);
});
});
describe('RECEIVE_SEARCH_RESULTS_ERROR', () => {
it('sets "projectSearchResult" to be empty', () => {
state.projectSearchResults = [''];
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.projectSearchResults).toHaveLength(0);
});
it('sets "messages.noResults" to be false', () => {
state.messages.noResults = true;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.messages.noResults).toBe(false);
});
it('sets "messages.searchError" to be true', () => {
state.messages.searchError = false;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.messages.searchError).toBe(true);
});
it('sets "messages.minimumQuery" to be false', () => {
state.messages.minimumQuery = true;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.messages.minimumQuery).toBe(false);
});
it('decrements "searchCount" by one', () => {
state.searchCount = 1;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.searchCount).toBe(0);
});
it('does not decrement "searchCount" into negative', () => {
state.searchCount = 0;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.searchCount).toBe(0);
});
});
describe('SET_MINIMUM_QUERY_MESSAGE', () => {
it('sets "projectSearchResult" to be an empty array', () => {
state.projectSearchResults = [''];
mutations[types.SET_MINIMUM_QUERY_MESSAGE](state);
expect(state.projectSearchResults).toHaveLength(0);
});
it('sets "messages.noResults" to be false', () => {
state.messages.noResults = true;
mutations[types.SET_MINIMUM_QUERY_MESSAGE](state);
expect(state.messages.noResults).toBe(false);
});
it('sets "messages.searchError" to be false', () => {
state.messages.searchError = true;
mutations[types.SET_MINIMUM_QUERY_MESSAGE](state);
expect(state.messages.searchError).toBe(false);
});
it('sets "messages.minimumQuery" to true', () => {
state.messages.minimumQuery = false;
mutations[types.SET_MINIMUM_QUERY_MESSAGE](state);
expect(state.messages.minimumQuery).toBe(true);
});
it('does not decrement "searchCount" into negative', () => {
state.searchCount = 0;
mutations[types.RECEIVE_SEARCH_RESULTS_ERROR](state);
expect(state.searchCount).toBe(0);
});
});
});
import createState from 'ee/security_dashboard/store/modules/project_selector/state';
describe('projectsSelector default state', () => {
const state = createState();
it('has "inputValue" set to be an empty string', () => {
expect(state.inputValue).toBe('');
});
it('has "isLoadingProjects" set to be false', () => {
expect(state.isLoadingProjects).toBe(false);
});
it('has "isAddingProjects" set to be false', () => {
expect(state.isAddingProjects).toBe(false);
});
it('has "isRemovingProject" set to be false', () => {
expect(state.isRemovingProject).toBe(false);
});
it('has all "projectEndpoints" set to be null', () => {
expect(state.projectEndpoints.list).toBe(null);
expect(state.projectEndpoints.add).toBe(null);
});
it('has "searchQuery" set to an empty string', () => {
expect(state.searchQuery).toBe('');
});
it('has "projects" set to be an empty array', () => {
expect(state.projects).toEqual([]);
});
it('has "projectSearchResults" set to be an empty array', () => {
expect(state.projectSearchResults).toEqual([]);
});
it('has "selectedProjects" set to be an empty array', () => {
expect(state.selectedProjects).toEqual([]);
});
it('has all "messages" set to be false', () => {
expect(state.messages.noResults).toBe(false);
expect(state.messages.searchError).toBe(false);
expect(state.messages.minimumQuery).toBe(false);
});
it('has "searchCount" set to be 0', () => {
expect(state.searchCount).toBe(0);
});
});
...@@ -14039,6 +14039,12 @@ msgstr "" ...@@ -14039,6 +14039,12 @@ msgstr ""
msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities." msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr "" msgstr ""
msgid "SecurityDashboard|%{firstProject} and %{secondProject}"
msgstr ""
msgid "SecurityDashboard|%{firstProject}, %{secondProject}, and %{rest}"
msgstr ""
msgid "SecurityDashboard|Confidence" msgid "SecurityDashboard|Confidence"
msgstr "" msgstr ""
...@@ -14060,6 +14066,9 @@ msgstr "" ...@@ -14060,6 +14066,9 @@ msgstr ""
msgid "SecurityDashboard|Severity" msgid "SecurityDashboard|Severity"
msgstr "" msgstr ""
msgid "SecurityDashboard|Unable to add %{invalidProjects}"
msgstr ""
msgid "See metrics" msgid "See metrics"
msgstr "" msgstr ""
...@@ -14785,6 +14794,9 @@ msgstr "" ...@@ -14785,6 +14794,9 @@ msgstr ""
msgid "Something went wrong, unable to add %{project} to dashboard" msgid "Something went wrong, unable to add %{project} to dashboard"
msgstr "" msgstr ""
msgid "Something went wrong, unable to add projects to dashboard"
msgstr ""
msgid "Something went wrong, unable to get projects" msgid "Something went wrong, unable to get projects"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment