Commit 440be5dd authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch '223189-remove-old-vuex-files' into 'master'

Remove unused files

See merge request gitlab-org/gitlab!38623
parents 0e826330 e90fcf1a
<script>
import { mapActions, mapState } from 'vuex';
import { GlDeprecatedButton, GlEmptyState, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
import ProjectManager from './project_manager.vue';
import SecurityDashboard from './security_dashboard_vuex.vue';
export default {
name: 'InstanceSecurityDashboard',
components: {
GlDeprecatedButton,
GlEmptyState,
GlLink,
GlLoadingIcon,
ProjectManager,
SecurityDashboard,
},
props: {
dashboardDocumentation: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
emptyDashboardStateSvgPath: {
type: String,
required: true,
},
projectAddEndpoint: {
type: String,
required: true,
},
projectListEndpoint: {
type: String,
required: true,
},
vulnerabilitiesEndpoint: {
type: String,
required: true,
},
vulnerabilitiesHistoryEndpoint: {
type: String,
required: true,
},
vulnerabilityFeedbackHelpPath: {
type: String,
required: true,
},
vulnerableProjectsEndpoint: {
type: String,
required: false,
default: '',
},
},
data() {
return {
isInitialized: false,
showProjectSelector: false,
};
},
computed: {
...mapState('projectSelector', ['projects']),
toggleButtonProps() {
return this.showProjectSelector
? {
variant: 'success',
text: s__('SecurityReports|Return to dashboard'),
}
: {
variant: 'secondary',
text: s__('SecurityReports|Edit dashboard'),
};
},
shouldShowEmptyState() {
return this.isInitialized && this.projects.length === 0;
},
},
created() {
this.setProjectEndpoints({
add: this.projectAddEndpoint,
list: this.projectListEndpoint,
});
this.fetchProjects()
// Failure to fetch projects will be handled in the store, so do nothing here.
.catch(() => {})
.finally(() => {
this.isInitialized = true;
});
},
methods: {
...mapActions('projectSelector', ['setProjectEndpoints', 'fetchProjects']),
toggleProjectSelector() {
this.showProjectSelector = !this.showProjectSelector;
},
},
};
</script>
<template>
<article>
<header class="page-title-holder flex-fill d-flex align-items-center">
<h2 class="page-title">{{ s__('SecurityReports|Security Dashboard') }}</h2>
<gl-deprecated-button
v-if="isInitialized"
class="page-title-controls js-project-selector-toggle"
:variant="toggleButtonProps.variant"
@click="toggleProjectSelector"
v-text="toggleButtonProps.text"
/>
</header>
<template v-if="isInitialized">
<project-manager v-if="showProjectSelector" />
<template v-else>
<gl-empty-state
v-if="shouldShowEmptyState"
:title="s__('SecurityReports|Add a project to your dashboard')"
:svg-path="emptyStateSvgPath"
>
<template #description>
{{
s__(
'SecurityReports|The security dashboard displays the latest security findings for projects you wish to monitor. Select "Edit dashboard" to add and remove projects.',
)
}}
<gl-link :href="dashboardDocumentation">{{
s__('SecurityReports|More information')
}}</gl-link
>.
</template>
<template #actions>
<gl-deprecated-button variant="success" @click="toggleProjectSelector">
{{ s__('SecurityReports|Add projects') }}
</gl-deprecated-button>
</template>
</gl-empty-state>
<security-dashboard
v-else
:vulnerabilities-endpoint="vulnerabilitiesEndpoint"
:vulnerabilities-history-endpoint="vulnerabilitiesHistoryEndpoint"
:vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath"
:vulnerable-projects-endpoint="vulnerableProjectsEndpoint"
/>
</template>
</template>
<gl-loading-icon v-else size="md" class="mt-4" />
</article>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { GlDeprecatedButton } from '@gitlab/ui';
import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue';
import ProjectList from './first_class_project_manager/project_list.vue';
export default {
components: {
GlDeprecatedButton,
ProjectList,
ProjectSelector,
},
computed: {
...mapState('projectSelector', [
'pageInfo',
'projects',
'selectedProjects',
'projectSearchResults',
'messages',
]),
...mapGetters('projectSelector', [
'canAddProjects',
'isSearchingProjects',
'isUpdatingProjects',
]),
},
methods: {
...mapActions('projectSelector', [
'fetchSearchResults',
'fetchSearchResultsNextPage',
'addProjects',
'clearSearchResults',
'toggleSelectedProject',
'setSearchQuery',
'removeProject',
]),
searched(query) {
this.setSearchQuery(query);
this.fetchSearchResults();
},
projectClicked(project) {
this.toggleSelectedProject(project);
},
projectRemoved(project) {
this.removeProject(project.remove_path);
},
},
};
</script>
<template>
<section class="container">
<div class="row justify-content-center mt-md-4">
<div class="col col-lg-7">
<h3 class="text-3 font-weight-bold border-bottom mb-4 pb-3">
{{ s__('SecurityReports|Add or remove projects from your dashboard') }}
</h3>
<div class="d-flex flex-column flex-md-row">
<project-selector
class="flex-grow mr-md-2"
:project-search-results="projectSearchResults"
:selected-projects="selectedProjects"
:show-no-results-message="messages.noResults"
:show-loading-indicator="isSearchingProjects"
:show-minimum-search-query-message="messages.minimumQuery"
:show-search-error-message="messages.searchError"
:total-results="pageInfo.total"
@searched="searched"
@projectClicked="projectClicked"
@bottomReached="fetchSearchResultsNextPage"
/>
<div class="mb-3">
<gl-deprecated-button
:disabled="!canAddProjects"
variant="success"
@click="addProjects"
>
{{ s__('SecurityReports|Add projects') }}
</gl-deprecated-button>
</div>
</div>
</div>
</div>
<div class="row justify-content-center mt-md-3">
<project-list
:projects="projects"
:show-loading-indicator="isUpdatingProjects"
class="col col-lg-7"
@projectRemoved="projectRemoved"
/>
</div>
</section>
</template>
<script>
import { isUndefined } from 'lodash';
import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import ReportsNotConfigured from './empty_states/reports_not_configured.vue';
import SecurityDashboard from './security_dashboard_vuex.vue';
export default {
components: {
GlEmptyState,
GlSprintf,
GlLink,
Icon,
ReportsNotConfigured,
SecurityDashboard,
TimeagoTooltip,
UserAvatarLink,
},
props: {
hasVulnerabilities: {
type: Boolean,
required: false,
default: false,
},
securityDashboardHelpPath: {
type: String,
required: false,
default: null,
},
commit: {
type: Object,
required: false,
default: () => ({}),
},
triggeredBy: {
type: Object,
required: false,
default: () => ({}),
},
branch: {
type: Object,
required: false,
default: () => ({}),
},
pipeline: {
type: Object,
required: false,
default: () => ({}),
},
project: {
type: Object,
required: false,
default: undefined,
validator: project => !isUndefined(project.id) && !isUndefined(project.name),
},
dashboardDocumentation: {
type: String,
required: false,
default: null,
},
emptyStateSvgPath: {
type: String,
required: false,
default: null,
},
vulnerabilityFeedbackHelpPath: {
type: String,
required: false,
default: null,
},
vulnerabilitiesEndpoint: {
type: String,
required: false,
default: null,
},
vulnerabilitiesSummaryEndpoint: {
type: String,
required: false,
default: null,
},
},
};
</script>
<template>
<div>
<template v-if="hasVulnerabilities">
<div class="card security-dashboard gl-mt-3">
<div class="card-header border-bottom-0">
<span class="js-security-dashboard-left">
<gl-sprintf
:message="
s__('SecurityReports|Pipeline %{pipelineLink} triggered %{timeago} by %{user}')
"
>
<template #pipelineLink>
<gl-link :href="pipeline.path">#{{ pipeline.id }}</gl-link>
</template>
<template #timeago>
<timeago-tooltip :time="pipeline.created" />
</template>
<template #user>
<user-avatar-link
:link-href="triggeredBy.path"
:img-src="triggeredBy.avatarPath"
:img-alt="triggeredBy.name"
:img-size="24"
:username="triggeredBy.name"
class="avatar-image-container"
/>
</template>
</gl-sprintf>
</span>
<span class="js-security-dashboard-right float-right">
<icon name="branch" />
<gl-link :href="branch.path" class="monospace">{{ branch.id }}</gl-link>
<span class="text-muted gl-ml-2 gl-mr-2">&middot;</span>
<icon name="commit" />
<gl-link :href="commit.path" class="monospace">{{ commit.id }}</gl-link>
</span>
</div>
</div>
<h4>{{ __('Vulnerabilities') }}</h4>
<security-dashboard
:lock-to-project="project"
:vulnerabilities-endpoint="vulnerabilitiesEndpoint"
:vulnerabilities-count-endpoint="vulnerabilitiesSummaryEndpoint"
:vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath"
>
<template #emptyState>
<gl-empty-state
:title="s__(`SecurityReports|No vulnerabilities found for this project`)"
:svg-path="emptyStateSvgPath"
:description="
s__(
`SecurityReports|While it's rare to have no vulnerabilities for your project, it can happen. In any event, we ask that you double check your settings to make sure you've set up your dashboard correctly.`,
)
"
:primary-button-link="dashboardDocumentation"
:primary-button-text="s__('SecurityReports|Learn more about setting up your dashboard')"
/>
</template>
</security-dashboard>
</template>
<reports-not-configured
v-else
:svg-path="emptyStateSvgPath"
:help-path="securityDashboardHelpPath"
/>
</div>
</template>
import Vue from 'vue';
import projectSelector from './store/plugins/project_selector';
import createStore from './store';
import { DASHBOARD_TYPES } from './store/constants';
import InstanceSecurityDashboard from './components/instance_security_dashboard.vue';
export default () => {
const el = document.querySelector('#js-security');
const {
dashboardDocumentation,
emptyStateSvgPath,
emptyDashboardStateSvgPath,
projectAddEndpoint,
projectListEndpoint,
vulnerabilitiesEndpoint,
vulnerabilitiesHistoryEndpoint,
vulnerabilityFeedbackHelpPath,
vulnerableProjectsEndpoint,
} = el.dataset;
const store = createStore({
dashboardType: DASHBOARD_TYPES.INSTANCE,
plugins: [projectSelector],
});
return new Vue({
el,
store,
render(createElement) {
return createElement(InstanceSecurityDashboard, {
props: {
dashboardDocumentation,
emptyStateSvgPath,
emptyDashboardStateSvgPath,
projectAddEndpoint,
projectListEndpoint,
vulnerabilitiesEndpoint,
vulnerabilitiesHistoryEndpoint,
vulnerabilityFeedbackHelpPath,
vulnerableProjectsEndpoint,
},
});
},
});
};
import Vue from 'vue';
import createStore from './store';
import { DASHBOARD_TYPES } from './store/constants';
import ProjectSecurityDashboard from './components/project_security_dashboard.vue';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
const securityTab = document.getElementById('js-security-report-app');
const props = {
...securityTab.dataset,
hasVulnerabilities: parseBoolean(securityTab.dataset.hasVulnerabilities),
};
if (props.hasVulnerabilities) {
Object.assign(props, {
project: {
id: props.projectId,
name: props.projectName,
},
triggeredBy: {
avatarPath: props.userAvatarPath,
name: props.userName,
path: props.userPath,
},
pipeline: {
id: parseInt(props.pipelineId, 10),
created: props.pipelineCreated,
path: props.pipelinePath,
},
commit: {
id: props.commitId,
path: props.commitPath,
},
branch: {
id: props.refId,
path: props.refPath,
},
});
}
const store = createStore({
dashboardType: DASHBOARD_TYPES.PROJECT,
});
return new Vue({
el: securityTab,
store,
render(createElement) {
return createElement(ProjectSecurityDashboard, {
props,
});
},
});
};
......@@ -158,25 +158,6 @@ export const fetchSearchResults = ({ state, dispatch, commit }) => {
.catch(() => dispatch('receiveSearchResultsError'));
};
export const fetchSearchResultsNextPage = ({ state, dispatch, commit }) => {
const {
searchQuery,
pageInfo: { totalPages, page, nextPage },
} = state;
if (totalPages <= page) {
return Promise.resolve();
}
const searchOptions = { page: nextPage };
return searchProjects(searchQuery, searchOptions)
.then(payload => {
commit(types.RECEIVE_NEXT_PAGE_SUCCESS, payload);
})
.catch(() => dispatch('receiveSearchResultsError'));
};
export const requestSearchResults = ({ commit }) => {
commit(types.REQUEST_SEARCH_RESULTS);
};
......
import state from './state';
import mutations from './mutations';
import * as actions from './actions';
import * as getters from './getters';
export default () => ({
namespaced: true,
state,
mutations,
actions,
getters,
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project Security Dashboard component Empty State renders correctly renders empty state component with correct props 1`] = `
Object {
"compact": false,
"description": "The security dashboard displays the latest security report. Use it to find and fix vulnerabilities.",
"primaryButtonLink": "http://test.host/help_dashboard",
"primaryButtonText": "Learn more",
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "http://test.host/img",
"title": "Monitor vulnerabilities in your code",
}
`;
exports[`Project Security Dashboard component Headline renders renders branch and commit information 1`] = `
<span
class="js-security-dashboard-right float-right"
>
<svg
aria-hidden="true"
class="s16 ic-branch"
>
<use
xlink:href="#branch"
/>
</svg>
<a
class="gl-link monospace"
href="http://test.host/branch"
>
master
</a>
<span
class="text-muted gl-ml-2 gl-mr-2"
>
·
</span>
<svg
aria-hidden="true"
class="s16 ic-commit"
>
<use
xlink:href="#commit"
/>
</svg>
<a
class="gl-link monospace"
href="http://test.host/commit"
>
1234adf
</a>
</span>
`;
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import InstanceSecurityDashboard from 'ee/security_dashboard/components/instance_security_dashboard.vue';
import SecurityDashboard from 'ee/security_dashboard/components/security_dashboard_vuex.vue';
import ProjectManager from 'ee/security_dashboard/components/project_manager.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
const dashboardDocumentation = '/help/docs';
const emptyStateSvgPath = '/svgs/empty.svg';
const emptyDashboardStateSvgPath = '/svgs/empty-dash.svg';
const projectAddEndpoint = '/projects/add';
const projectListEndpoint = '/projects/list';
const vulnerabilitiesEndpoint = '/vulnerabilities';
const vulnerabilitiesHistoryEndpoint = '/vulnerabilities_history';
const vulnerabilityFeedbackHelpPath = '/vulnerabilities_feedback_help';
const vulnerableProjectsEndpoint = '/vulnerable_projects_endpoint';
describe('Instance Security Dashboard component', () => {
let store;
let wrapper;
let actionResolvers;
const factory = ({ projects = [] } = {}) => {
store = new Vuex.Store({
modules: {
projectSelector: {
namespaced: true,
actions: {
fetchProjects() {},
setProjectEndpoints() {},
},
state: {
projects,
},
},
},
});
actionResolvers = [];
jest.spyOn(store, 'dispatch').mockImplementation(
() =>
new Promise(resolve => {
actionResolvers.push(resolve);
}),
);
wrapper = shallowMount(InstanceSecurityDashboard, {
localVue,
store,
propsData: {
dashboardDocumentation,
emptyStateSvgPath,
emptyDashboardStateSvgPath,
projectAddEndpoint,
projectListEndpoint,
vulnerabilitiesEndpoint,
vulnerabilitiesHistoryEndpoint,
vulnerabilityFeedbackHelpPath,
vulnerableProjectsEndpoint,
},
});
};
const resolveActions = () => {
actionResolvers.forEach(resolve => resolve());
};
const findProjectSelectorToggleButton = () => wrapper.find('.js-project-selector-toggle');
const clickProjectSelectorToggleButton = () => {
findProjectSelectorToggleButton().vm.$emit('click');
return wrapper.vm.$nextTick();
};
const expectComponentWithProps = (Component, props) => {
const componentWrapper = wrapper.find(Component);
expect(componentWrapper.exists()).toBe(true);
expect(componentWrapper.props()).toEqual(expect.objectContaining(props));
};
const expectProjectSelectorState = () => {
expect(findProjectSelectorToggleButton().exists()).toBe(true);
expect(wrapper.find(GlEmptyState).exists()).toBe(false);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(SecurityDashboard).exists()).toBe(false);
expect(wrapper.find(ProjectManager).exists()).toBe(true);
};
afterEach(() => {
wrapper.destroy();
});
describe('on creation', () => {
beforeEach(() => {
factory();
});
it('dispatches the expected actions', () => {
expect(store.dispatch.mock.calls).toEqual([
[
'projectSelector/setProjectEndpoints',
{
add: projectAddEndpoint,
list: projectListEndpoint,
},
],
['projectSelector/fetchProjects', undefined],
]);
});
it('displays the initial loading state', () => {
expect(findProjectSelectorToggleButton().exists()).toBe(false);
expect(wrapper.find(GlEmptyState).exists()).toBe(false);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.find(SecurityDashboard).exists()).toBe(false);
expect(wrapper.find(ProjectManager).exists()).toBe(false);
});
});
describe('given there are no projects', () => {
beforeEach(() => {
factory();
resolveActions();
});
it('renders the empty state', () => {
expect(findProjectSelectorToggleButton().exists()).toBe(true);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(SecurityDashboard).exists()).toBe(false);
expect(wrapper.find(ProjectManager).exists()).toBe(false);
expectComponentWithProps(GlEmptyState, {
svgPath: emptyStateSvgPath,
});
});
describe('after clicking the project selector toggle button', () => {
beforeEach(clickProjectSelectorToggleButton);
it('renders the project selector state', () => {
expectProjectSelectorState();
});
});
});
describe('given there are projects', () => {
beforeEach(() => {
factory({ projects: [{ name: 'foo', id: 1 }] });
resolveActions();
});
it('renders the security dashboard state', () => {
expect(findProjectSelectorToggleButton().exists()).toBe(true);
expect(wrapper.find(GlEmptyState).exists()).toBe(false);
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find(ProjectManager).exists()).toBe(false);
expectComponentWithProps(SecurityDashboard, {
vulnerabilitiesEndpoint,
vulnerabilitiesHistoryEndpoint,
vulnerabilityFeedbackHelpPath,
vulnerableProjectsEndpoint,
});
});
describe('after clicking the project selector toggle button', () => {
beforeEach(clickProjectSelectorToggleButton);
it('renders the project selector state', () => {
expectProjectSelectorState();
});
});
});
});
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { merge } from 'lodash';
import createDefaultState from 'ee/security_dashboard/store/modules/project_selector/state';
import { GlDeprecatedButton } from '@gitlab/ui';
import ProjectManager from 'ee/security_dashboard/components/project_manager.vue';
import ProjectList from 'ee/security_dashboard/components/first_class_project_manager/project_list.vue';
import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Project Manager component', () => {
let storeOptions;
let store;
let wrapper;
const factory = ({
state = {},
canAddProjects = false,
isSearchingProjects = false,
isUpdatingProjects = false,
} = {}) => {
storeOptions = {
modules: {
projectSelector: {
namespaced: true,
actions: {
setSearchQuery: jest.fn(),
fetchSearchResults: jest.fn(),
fetchSearchResultsNextPage: jest.fn(),
addProjects: jest.fn(),
clearSearchResults: jest.fn(),
toggleSelectedProject: jest.fn(),
removeProject: jest.fn(),
},
getters: {
canAddProjects: jest.fn().mockReturnValue(canAddProjects),
isSearchingProjects: jest.fn().mockReturnValue(isSearchingProjects),
isUpdatingProjects: jest.fn().mockReturnValue(isUpdatingProjects),
},
state: {
...merge(createDefaultState(), state),
},
},
},
};
store = new Vuex.Store(storeOptions);
wrapper = shallowMount(ProjectManager, {
localVue,
store,
});
};
const getMockAction = actionName => storeOptions.modules.projectSelector.actions[actionName];
const getMockActionDispatchedPayload = actionName => getMockAction(actionName).mock.calls[0][1];
const getAddProjectsButton = () => wrapper.find(GlDeprecatedButton);
const getProjectList = () => wrapper.find(ProjectList);
const getProjectSelector = () => wrapper.find(ProjectSelector);
afterEach(() => {
wrapper.destroy();
});
describe('given the default state', () => {
beforeEach(factory);
it('contains a project-selector component', () => {
expect(getProjectSelector().exists()).toBe(true);
});
it.each`
actionName | payload
${'setSearchQuery'} | ${'foo'}
${'fetchSearchResults'} | ${undefined}
`(
'dispatches the correct actions when a project-search has been triggered',
({ actionName, payload }) => {
getProjectSelector().vm.$emit('searched', payload);
expect(getMockActionDispatchedPayload(actionName)).toBe(payload);
},
);
it('contains a button for adding selected projects', () => {
expect(getAddProjectsButton().text()).toContain('Add projects');
});
it('disables the button for adding projects per default', () => {
expect(getAddProjectsButton().attributes('disabled')).toBe('true');
});
it('dispatches the addProjects when the "Add projects" button has been clicked', () => {
getAddProjectsButton().vm.$emit('click');
expect(getMockAction('addProjects')).toHaveBeenCalled();
});
it('contains a project-list component', () => {
expect(getProjectList().exists()).toBe(true);
});
it('dispatches the right actions when the project-list emits a "bottomReached" event', () => {
const projectSelector = getProjectSelector();
const fetchSearchResultsNextPageAction = getMockAction('fetchSearchResultsNextPage');
expect(fetchSearchResultsNextPageAction).toHaveBeenCalledTimes(0);
projectSelector.vm.$emit('bottomReached');
expect(fetchSearchResultsNextPageAction).toHaveBeenCalledTimes(1);
});
it('dispatches the right actions when the project-list emits a "projectRemoved" event', () => {
const mockProject = { remove_path: 'foo' };
const projectList = getProjectList();
const removeProjectAction = getMockAction('removeProject');
expect(removeProjectAction).toHaveBeenCalledTimes(0);
projectList.vm.$emit('projectRemoved', mockProject);
expect(removeProjectAction).toHaveBeenCalledTimes(1);
expect(removeProjectAction.mock.calls[0][1]).toBe(mockProject.remove_path);
});
});
describe('given the store state', () => {
it.each`
config | projectSelectorPropName | expectedPropValue
${{ isSearchingProjects: true }} | ${'showLoadingIndicator'} | ${true}
${{ state: { selectedProjects: ['bar'] } }} | ${'selectedProjects'} | ${['bar']}
${{ state: { projectSearchResults: ['foo'] } }} | ${'projectSearchResults'} | ${['foo']}
${{ state: { messages: { noResults: true } } }} | ${'showNoResultsMessage'} | ${true}
${{ state: { messages: { searchError: true } } }} | ${'showSearchErrorMessage'} | ${true}
${{ state: { messages: { minimumQuery: true } } }} | ${'showMinimumSearchQueryMessage'} | ${true}
`(
'passes $projectSelectorPropName = $expectedPropValue to the project-selector',
({ config, projectSelectorPropName, expectedPropValue }) => {
factory(config);
expect(getProjectSelector().props(projectSelectorPropName)).toEqual(expectedPropValue);
},
);
it('enables the add-projects button when projects can be added', () => {
factory({ canAddProjects: true });
expect(getAddProjectsButton().attributes('disabled')).toBe(undefined);
});
it('passes the list of projects to the project-list component', () => {
const projects = [{ foo: true }];
factory({ state: { projects } });
expect(getProjectList().props('projects')).toEqual(projects);
});
it.each([false, true])(
'passes showLoadingIndicator = %p to the project-list component',
isUpdatingProjects => {
factory({ isUpdatingProjects });
expect(getProjectList().props('showLoadingIndicator')).toBe(isUpdatingProjects);
},
);
});
});
import { mount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import ProjectSecurityDashboard from 'ee/security_dashboard/components/project_security_dashboard.vue';
import createStore from 'ee/security_dashboard/store';
import { trimText } from 'helpers/text_helper';
import axios from '~/lib/utils/axios_utils';
const vulnerabilitiesEndpoint = `${TEST_HOST}/vulnerabilities`;
const vulnerabilitiesSummaryEndpoint = `${TEST_HOST}/vulnerabilities_summary`;
describe('Project Security Dashboard component', () => {
let wrapper;
let mock;
const runDate = new Date();
runDate.setDate(runDate.getDate() - 7);
const createComponent = props => {
wrapper = mount(ProjectSecurityDashboard, {
store: createStore(),
stubs: ['security-dashboard-table'],
provide: {
emptyStateSvgPath: `${TEST_HOST}/img`,
},
propsData: {
hasVulnerabilities: true,
securityDashboardHelpPath: `${TEST_HOST}/help_dashboard`,
commit: {
id: '1234adf',
path: `${TEST_HOST}/commit`,
},
branch: {
id: 'master',
path: `${TEST_HOST}/branch`,
},
pipeline: {
id: '55',
created: runDate.toISOString(),
path: `${TEST_HOST}/pipeline`,
},
triggeredBy: {
path: `${TEST_HOST}/user`,
avatarPath: `${TEST_HOST}/img`,
name: 'TestUser',
},
project: {
id: 123,
name: 'my-project',
},
vulnerabilityFeedbackHelpPath: `${TEST_HOST}/vulnerability_feedback_help`,
vulnerabilitiesEndpoint,
vulnerabilitiesSummaryEndpoint,
...props,
},
});
};
beforeEach(() => {
mock = new MockAdapter(axios);
createComponent();
});
afterEach(() => {
wrapper.destroy();
mock.restore();
});
describe('Headline renders', () => {
it('renders pipeline metadata information', () => {
const element = wrapper.find('.card-header .js-security-dashboard-left');
expect(trimText(element.text())).toBe('Pipeline #55 triggered 1 week ago by TestUser');
const pipelineLink = element.find(`a[href="${TEST_HOST}/pipeline"]`);
expect(pipelineLink).not.toBeNull();
expect(pipelineLink.text()).toBe('#55');
const userAvatarLink = element.find('a.user-avatar-link');
expect(userAvatarLink).not.toBeNull();
expect(userAvatarLink.attributes('href')).toBe(`${TEST_HOST}/user`);
expect(userAvatarLink.find('img').attributes('src')).toBe(`${TEST_HOST}/img?width=24`);
expect(userAvatarLink.text().trim()).toBe('TestUser');
});
it('renders branch and commit information', () => {
const revInformation = wrapper.find('.card-header .js-security-dashboard-right');
expect(revInformation.element).toMatchSnapshot();
});
});
describe('Dashboard renders properly', () => {
const findDashboard = () => wrapper.find(ProjectSecurityDashboard);
it('renders security dashboard', () => {
const dashboard = findDashboard();
expect(dashboard.exists()).toBe(true);
});
it('renders one filter less because projects filter is locked', () => {
const dashboard = findDashboard();
const filters = dashboard.findAll('.dashboard-filter');
expect(filters).toHaveLength(wrapper.vm.$store.state.filters.filters.length - 1);
});
});
describe('Empty State renders correctly', () => {
beforeEach(() => {
createComponent({ hasVulnerabilities: false });
});
it('renders empty state component with correct props', () => {
const emptyState = wrapper.find(GlEmptyState);
expect(emptyState.exists()).toBe(true);
expect(emptyState.props()).toMatchSnapshot();
});
});
});
......@@ -574,59 +574,6 @@ describe('EE projectSelector actions', () => {
));
});
describe('fetchSearchResultsNextPage', () => {
describe('when the current page-index is smaller than the number of total pages', () => {
beforeEach(() => {
state.pageInfo.totalPages = 2;
state.pageInfo.page = 1;
});
it('dispatches the "receiveNextPageSuccess" action if the request is successful', () => {
const projects = [{ id: 0, name: 'mock-name1' }];
mockAxios.onGet().replyOnce(200, projects, responseHeaders);
return testAction(
actions.fetchSearchResultsNextPage,
null,
state,
[
{
type: types.RECEIVE_NEXT_PAGE_SUCCESS,
payload: { data: projects, headers: responseHeaders, pageInfo },
},
],
[],
);
});
it('dispatches the "receiveSearchResultsError" action if the request is not successful', () => {
mockAxios.onGet(mockListEndpoint).replyOnce(500);
return testAction(
actions.fetchSearchResultsNextPage,
null,
state,
[],
[
{
type: 'receiveSearchResultsError',
},
],
);
});
});
describe('when the current page-index is equal to the number of total pages', () => {
it('does not commit any mutations or dispatch any actions', () => {
state.pageInfo.totalPages = 1;
state.pageInfo.page = 1;
return testAction(actions.fetchSearchResultsNextPage, [], state);
});
});
});
describe('setProjectEndpoints', () => {
it('commits project list and add endpoints', () => {
const payload = {
......
......@@ -21427,15 +21427,9 @@ msgstr ""
msgid "SecurityReports|No vulnerabilities found for this pipeline"
msgstr ""
msgid "SecurityReports|No vulnerabilities found for this project"
msgstr ""
msgid "SecurityReports|Oops, something doesn't seem right."
msgstr ""
msgid "SecurityReports|Pipeline %{pipelineLink} triggered %{timeago} by %{user}"
msgstr ""
msgid "SecurityReports|Project"
msgstr ""
......@@ -21529,9 +21523,6 @@ msgstr ""
msgid "SecurityReports|While it's rare to have no vulnerabilities for your pipeline, it can happen. In any event, we ask that you double check your settings to make sure all security scanning jobs have passed successfully."
msgstr ""
msgid "SecurityReports|While it's rare to have no vulnerabilities for your project, it can happen. In any event, we ask that you double check your settings to make sure you've set up your dashboard correctly."
msgstr ""
msgid "SecurityReports|While it's rare to have no vulnerabilities, it can happen. In any event, we ask that you double check your settings to make sure you've set up your dashboard correctly."
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