Commit 5a69d75a authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'extract-common-store-from-operations-dashbord' into 'master'

Extract common store from operations dashbord

Closes #11443 and #3713

See merge request gitlab-org/gitlab-ee!11702
parents 3a0bcca8 a9b23d10
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapState, mapActions } from 'vuex';
import { import {
GlLoadingIcon, GlLoadingIcon,
GlModal, GlModal,
...@@ -59,19 +60,17 @@ export default { ...@@ -59,19 +60,17 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
projects: [],
projectTokens: '',
isLoadingProjects: false,
selectedProjects: [],
projectSearchResults: [],
searchCount: 0,
searchQuery: '',
messages: {},
};
},
computed: { computed: {
...mapState([
'projects',
'projectTokens',
'isLoadingProjects',
'selectedProjects',
'projectSearchResults',
'searchCount',
'searchQuery',
'messages',
]),
isSearchingProjects() { isSearchingProjects() {
return this.searchCount > 0; return this.searchCount > 0;
}, },
...@@ -87,13 +86,15 @@ export default { ...@@ -87,13 +86,15 @@ export default {
this.fetchProjects(); this.fetchProjects();
}, },
methods: { methods: {
fetchSearchResults() {}, ...mapActions([
addProjectsToDashboard() {}, 'fetchSearchResults',
fetchProjects() {}, 'addProjectsToDashboard',
setProjectEndpoints() {}, 'fetchProjects',
clearSearchResults() {}, 'setProjectEndpoints',
toggleSelectedProject() {}, 'clearSearchResults',
setSearchQuery() {}, 'toggleSelectedProject',
'setSearchQuery',
]),
addProjects() { addProjects() {
this.addProjectsToDashboard(); this.addProjectsToDashboard();
}, },
......
import Vue from 'vue'; import Vue from 'vue';
import EnvironmentDashboardComponent from 'ee/environments_dashboard/components/dashboard/dashboard.vue'; import EnvironmentDashboardComponent from 'ee/environments_dashboard/components/dashboard/dashboard.vue';
import createStore from 'ee/vue_shared/dashboards/store';
document.addEventListener( document.addEventListener(
'DOMContentLoaded', 'DOMContentLoaded',
() => () =>
new Vue({ new Vue({
el: '#js-environments', el: '#js-environments',
store: createStore(),
components: { components: {
EnvironmentDashboardComponent, EnvironmentDashboardComponent,
}, },
......
import Vue from 'vue'; import Vue from 'vue';
import store from 'ee/operations/store'; import createStore from 'ee/vue_shared/dashboards/store';
import DashboardComponent from 'ee/operations/components/dashboard/dashboard.vue'; import DashboardComponent from 'ee/operations/components/dashboard/dashboard.vue';
document.addEventListener( document.addEventListener(
...@@ -7,7 +7,7 @@ document.addEventListener( ...@@ -7,7 +7,7 @@ document.addEventListener(
() => () =>
new Vue({ new Vue({
el: '#js-operations', el: '#js-operations',
store, store: createStore(),
components: { components: {
DashboardComponent, DashboardComponent,
}, },
......
...@@ -48,24 +48,32 @@ export const receiveAddProjectsToDashboardSuccess = ({ dispatch, state }, data) ...@@ -48,24 +48,32 @@ export const receiveAddProjectsToDashboardSuccess = ({ dispatch, state }, data)
const { added, invalid } = data; const { added, invalid } = data;
if (invalid.length) { if (invalid.length) {
const projectNames = state.selectedProjects.reduce((accumulator, project) => { const [firstProject, secondProject, ...rest] = state.selectedProjects
if (invalid.includes(project.id)) { .filter(project => invalid.includes(project.id))
accumulator.push(project.name); .map(project => project.name);
} const translationValues = {
return accumulator; firstProject,
}, []); secondProject,
rest: rest.join(', '),
};
let invalidProjects; let invalidProjects;
if (projectNames.length > 2) { if (rest.length > 0) {
invalidProjects = `${projectNames.slice(0, -1).join(', ')}, and ${projectNames.pop()}`; invalidProjects = sprintf(
} else if (projectNames.length > 1) { s__('Dashboard|%{firstProject}, %{rest}, and %{secondProject}'),
invalidProjects = projectNames.join(' and '); translationValues,
);
} else if (secondProject) {
invalidProjects = sprintf(
s__('Dashboard|%{firstProject} and %{secondProject}'),
translationValues,
);
} else { } else {
[invalidProjects] = projectNames; invalidProjects = firstProject;
} }
createFlash( createFlash(
sprintf( sprintf(
s__( s__(
'OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan.', 'Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Gold plan.',
), ),
{ {
invalidProjects, invalidProjects,
...@@ -124,7 +132,7 @@ export const receiveProjectsSuccess = ({ commit }, data) => { ...@@ -124,7 +132,7 @@ export const receiveProjectsSuccess = ({ commit }, data) => {
export const receiveProjectsError = ({ commit }) => { export const receiveProjectsError = ({ commit }) => {
commit(types.RECEIVE_PROJECTS_ERROR); commit(types.RECEIVE_PROJECTS_ERROR);
createFlash(__('Something went wrong, unable to get operations projects')); createFlash(__('Something went wrong, unable to get projects'));
}; };
export const removeProject = ({ dispatch }, removePath) => { export const removeProject = ({ dispatch }, removePath) => {
......
...@@ -6,8 +6,9 @@ import * as actions from './actions'; ...@@ -6,8 +6,9 @@ import * as actions from './actions';
Vue.use(Vuex); Vue.use(Vuex);
export default new Vuex.Store({ export default () =>
state, new Vuex.Store({
mutations, state,
actions, mutations,
}); actions,
});
---
title: Add Frontend Store and UI For Environments Dashboard MVC
merge_request: 11702
author:
type: added
...@@ -4,9 +4,19 @@ exports[`dashboard should match the snapshot 1`] = ` ...@@ -4,9 +4,19 @@ exports[`dashboard should match the snapshot 1`] = `
<div <div
class="operations-dashboard" class="operations-dashboard"
> >
<div> <glmodal-stub
<!----> modalid="add-projects-modal"
</div> ok-disabled="true"
ok-title="Add projects"
ok-variant="success"
title="Add projects"
titletag="h4"
>
<projectselector-stub
projectsearchresults=""
selectedprojects=""
/>
</glmodal-stub>
<div <div
class="page-title-holder flex-fill d-flex align-items-center" class="page-title-holder flex-fill d-flex align-items-center"
...@@ -19,14 +29,14 @@ exports[`dashboard should match the snapshot 1`] = ` ...@@ -19,14 +29,14 @@ exports[`dashboard should match the snapshot 1`] = `
</h1> </h1>
<button <glbutton-stub
class="btn js-add-projects-button btn btn-success btn-secondary" class="js-add-projects-button btn btn-success"
type="button" role="button"
> >
Add projects Add projects
</button> </glbutton-stub>
</div> </div>
<div <div
......
import { mount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { GlButton } from '@gitlab/ui'; import { GlButton, GlModal } from '@gitlab/ui';
import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue'; import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue';
import createStore from 'ee/vue_shared/dashboards/store/index';
import state from 'ee/vue_shared/dashboards/store/state';
import component from 'ee/environments_dashboard/components/dashboard/dashboard.vue'; import component from 'ee/environments_dashboard/components/dashboard/dashboard.vue';
import ProjectHeader from 'ee/environments_dashboard/components/dashboard/project_header.vue'; import ProjectHeader from 'ee/environments_dashboard/components/dashboard/project_header.vue';
import Environment from 'ee/environments_dashboard/components/dashboard/environment.vue'; import Environment from 'ee/environments_dashboard/components/dashboard/environment.vue';
...@@ -13,10 +15,19 @@ localVue.use(Vuex); ...@@ -13,10 +15,19 @@ localVue.use(Vuex);
describe('dashboard', () => { describe('dashboard', () => {
const Component = localVue.extend(component); const Component = localVue.extend(component);
let actionSpies;
const store = createStore();
let wrapper; let wrapper;
let propsData; let propsData;
beforeEach(() => { beforeEach(() => {
actionSpies = {
addProjectsToDashboard: jest.fn(),
clearSearchResults: jest.fn(),
setSearchQuery: jest.fn(),
fetchSearchResults: jest.fn(),
toggleSelectedProject: jest.fn(),
};
propsData = { propsData = {
addPath: 'mock-addPath', addPath: 'mock-addPath',
listPath: 'mock-listPath', listPath: 'mock-listPath',
...@@ -24,16 +35,21 @@ describe('dashboard', () => { ...@@ -24,16 +35,21 @@ describe('dashboard', () => {
emptyDashboardHelpPath: '/help/user/operations_dashboard/index.html', emptyDashboardHelpPath: '/help/user/operations_dashboard/index.html',
}; };
wrapper = mount(Component, { wrapper = shallowMount(Component, {
propsData, propsData,
localVue, localVue,
store,
methods: { methods: {
fetchProjects: () => {}, fetchProjects: () => {},
...actionSpies,
}, },
sync: false,
}); });
}); });
afterEach(() => {
store.replaceState(state());
});
it('should match the snapshot', () => { it('should match the snapshot', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
...@@ -52,19 +68,11 @@ describe('dashboard', () => { ...@@ -52,19 +68,11 @@ describe('dashboard', () => {
it('is labelled correctly', () => { it('is labelled correctly', () => {
expect(button.text()).toBe('Add projects'); expect(button.text()).toBe('Add projects');
}); });
it('should show the modal on click', done => {
button.trigger('click');
wrapper.vm.$nextTick(() => {
expect(wrapper.find(ProjectSelector)).toExist();
done();
});
});
}); });
describe('wrapped components', () => { describe('wrapped components', () => {
beforeEach(done => { beforeEach(() => {
wrapper.vm.projects = [ store.state.projects = [
{ {
id: 0, id: 0,
name: 'test', name: 'test',
...@@ -73,7 +81,6 @@ describe('dashboard', () => { ...@@ -73,7 +81,6 @@ describe('dashboard', () => {
}, },
{ id: 1, name: 'test', namespace: { name: 'test', id: 0 }, environments: [environment] }, { id: 1, name: 'test', namespace: { name: 'test', id: 0 }, environments: [environment] },
]; ];
wrapper.vm.$nextTick(() => done());
}); });
describe('project header', () => { describe('project header', () => {
...@@ -89,5 +96,36 @@ describe('dashboard', () => { ...@@ -89,5 +96,36 @@ describe('dashboard', () => {
expect(environments.length).toBe(3); expect(environments.length).toBe(3);
}); });
}); });
describe('project selector modal', () => {
beforeEach(() => {
wrapper.find(GlButton).trigger('click');
});
it('should fire the add projects action on ok', () => {
wrapper.find(GlModal).vm.$emit('ok');
expect(actionSpies.addProjectsToDashboard).toHaveBeenCalled();
});
it('should fire clear search when the modal is hidden', () => {
wrapper.find(GlModal).vm.$emit('hidden');
expect(actionSpies.clearSearchResults).toHaveBeenCalled();
});
it('should set the search query when searching', () => {
wrapper.find(ProjectSelector).vm.$emit('searched', 'test');
expect(actionSpies.setSearchQuery).toHaveBeenCalledWith('test');
});
it('should fetch query results when searching', () => {
wrapper.find(ProjectSelector).vm.$emit('searched', 'test');
expect(actionSpies.fetchSearchResults).toHaveBeenCalled();
});
it('should toggle a project when clicked', () => {
wrapper.find(ProjectSelector).vm.$emit('projectClicked', { name: 'test', id: 1 });
expect(actionSpies.toggleSelectedProject).toHaveBeenCalledWith({ name: 'test', id: 1 });
});
});
}); });
}); });
...@@ -4,7 +4,7 @@ import { mount, createLocalVue } from '@vue/test-utils'; ...@@ -4,7 +4,7 @@ import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import Project from 'ee/operations/components/dashboard/project.vue'; import Project from 'ee/operations/components/dashboard/project.vue';
import Dashboard from 'ee/operations/components/dashboard/dashboard.vue'; import Dashboard from 'ee/operations/components/dashboard/dashboard.vue';
import store from 'ee/operations/store'; import createStore from 'ee/vue_shared/dashboards/store';
import timeoutPromise from 'spec/helpers/set_timeout_promise_helper'; import timeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { trimText } from 'spec/helpers/vue_component_helper'; import { trimText } from 'spec/helpers/vue_component_helper';
import { mockProjectData, mockText } from '../../mock_data'; import { mockProjectData, mockText } from '../../mock_data';
...@@ -16,6 +16,7 @@ describe('dashboard component', () => { ...@@ -16,6 +16,7 @@ describe('dashboard component', () => {
const mockAddEndpoint = 'mock-addPath'; const mockAddEndpoint = 'mock-addPath';
const mockListEndpoint = 'mock-listPath'; const mockListEndpoint = 'mock-listPath';
const DashboardComponent = localVue.extend(Dashboard); const DashboardComponent = localVue.extend(Dashboard);
const store = createStore();
let wrapper; let wrapper;
let mockAxios; let mockAxios;
...@@ -83,7 +84,7 @@ describe('dashboard component', () => { ...@@ -83,7 +84,7 @@ describe('dashboard component', () => {
.then(timeoutPromise) .then(timeoutPromise)
.then(() => { .then(() => {
expect(store.state.projects.length).toEqual(2); expect(store.state.projects.length).toEqual(2);
expect(wrapper.element.querySelectorAll('.js-dashboard-project').length).toEqual(2); expect(wrapper.findAll(Project).length).toEqual(2);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
...@@ -102,7 +103,7 @@ describe('dashboard component', () => { ...@@ -102,7 +103,7 @@ describe('dashboard component', () => {
}); });
it('includes a dashboard project component for each project', () => { it('includes a dashboard project component for each project', () => {
const projectComponents = wrapper.element.querySelectorAll('.js-dashboard-project'); const projectComponents = wrapper.findAll(Project);
expect(projectComponents.length).toBe(projectCount); expect(projectComponents.length).toBe(projectCount);
}); });
...@@ -125,7 +126,7 @@ describe('dashboard component', () => { ...@@ -125,7 +126,7 @@ describe('dashboard component', () => {
timeoutPromise() timeoutPromise()
.then(() => { .then(() => {
expect(store.state.projects.length).toEqual(0); expect(store.state.projects.length).toEqual(0);
expect(wrapper.element.querySelectorAll('.js-dashboard-project').length).toEqual(0); expect(wrapper.findAll(Project).length).toEqual(0);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
......
...@@ -4,7 +4,7 @@ import Commit from '~/vue_shared/components/commit.vue'; ...@@ -4,7 +4,7 @@ import Commit from '~/vue_shared/components/commit.vue';
import Project from 'ee/operations/components/dashboard/project.vue'; import Project from 'ee/operations/components/dashboard/project.vue';
import ProjectHeader from 'ee/operations/components/dashboard/project_header.vue'; import ProjectHeader from 'ee/operations/components/dashboard/project_header.vue';
import Alerts from 'ee/vue_shared/dashboards/components/alerts.vue'; import Alerts from 'ee/vue_shared/dashboards/components/alerts.vue';
import store from 'ee/operations/store'; import store from 'ee/vue_shared/dashboards/store';
import { mockOneProject } from '../../mock_data'; import { mockOneProject } from '../../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
import state from 'ee/operations/store/state';
export function clearState(store) {
store.replaceState(state());
}
/** /**
* @deprecated * @deprecated
* DO NOT USE! This causes issues when `vue-test-utils` is used elsewhere. * DO NOT USE! This causes issues when `vue-test-utils` is used elsewhere.
......
import mockPipelineData from 'ee_spec/vue_shared/dashboards/mock_data'; import { mockProjectData, mockText as text } from 'ee_spec/vue_shared/dashboards/mock_data';
export { mockProjectData } from 'ee_spec/vue_shared/dashboards/mock_data';
export const mockText = { export const mockText = {
...text,
ADD_PROJECTS: 'Add projects', ADD_PROJECTS: 'Add projects',
ADD_PROJECTS_ERROR: 'Something went wrong, unable to add projects to dashboard',
REMOVE_PROJECT_ERROR: 'Something went wrong, unable to remove project',
DASHBOARD_TITLE: 'Operations Dashboard', DASHBOARD_TITLE: 'Operations Dashboard',
EMPTY_TITLE: 'Add a project to the dashboard', EMPTY_TITLE: 'Add a project to the dashboard',
EMPTY_SUBTITLE: EMPTY_SUBTITLE:
"The operations dashboard provides a summary of each project's operational health, including pipeline and alert statuses. More information", "The operations dashboard provides a summary of each project's operational health, including pipeline and alert statuses. More information",
EMPTY_SVG_SOURCE: '/assets/illustrations/operations-dashboard_empty.svg', EMPTY_SVG_SOURCE: '/assets/illustrations/operations-dashboard_empty.svg',
NO_SEARCH_RESULTS: 'Sorry, no projects matched your search',
RECEIVE_PROJECTS_ERROR: 'Something went wrong, unable to get operations projects',
REMOVE_PROJECT: 'Remove', REMOVE_PROJECT: 'Remove',
SEARCH_PROJECTS: 'Search your projects', SEARCH_PROJECTS: 'Search your projects',
SEARCH_DESCRIPTION_SUFFIX: 'in projects', SEARCH_DESCRIPTION_SUFFIX: 'in projects',
}; };
export function mockProjectData(
projectCount = 1,
currentPipelineStatus = 'success',
upstreamStatus = 'success',
alertCount = 0,
) {
return Array(projectCount)
.fill(null)
.map((_, index) => ({
id: index,
description: '',
name: 'test-project',
name_with_namespace: 'Test / test-project',
path: 'test-project',
path_with_namespace: 'test/test-project',
created_at: '2019-02-01T15:40:27.522Z',
default_branch: 'master',
tag_list: [],
avatar_url: null,
web_url: 'https://mock-web_url/',
namespace: {
id: 1,
name: 'test',
path: 'test',
kind: 'user',
full_path: 'user',
parent_id: null,
},
remove_path: '/-/operations?project_id=1',
last_pipeline: mockPipelineData(currentPipelineStatus),
upstream_pipeline: mockPipelineData(upstreamStatus),
downstream_pipelines: [],
alert_count: alertCount,
}));
}
export const [mockOneProject] = mockProjectData(1); export const [mockOneProject] = mockProjectData(1);
import { mount, createLocalVue } from '@vue/test-utils'; import { mount, createLocalVue } from '@vue/test-utils';
import ProjectPipeline from 'ee/vue_shared/dashboards/components/project_pipeline.vue'; import ProjectPipeline from 'ee/vue_shared/dashboards/components/project_pipeline.vue';
import mockPipelineData from '../mock_data'; import { mockPipelineData } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
import state from 'ee/vue_shared/dashboards/store/state';
export default function clearState(store) {
store.replaceState(state());
}
...@@ -2,7 +2,14 @@ import { TEST_HOST } from 'spec/test_constants'; ...@@ -2,7 +2,14 @@ import { TEST_HOST } from 'spec/test_constants';
const AVATAR_URL = `${TEST_HOST}/dummy.jpg`; const AVATAR_URL = `${TEST_HOST}/dummy.jpg`;
export default function mockPipelineData( export const mockText = {
ADD_PROJECTS_ERROR: 'Something went wrong, unable to add projects to dashboard',
REMOVE_PROJECT_ERROR: 'Something went wrong, unable to remove project',
NO_SEARCH_RESULTS: 'Sorry, no projects matched your search',
RECEIVE_PROJECTS_ERROR: 'Something went wrong, unable to get projects',
};
export function mockPipelineData(
status = 'success', status = 'success',
id = 1, id = 1,
finishedTimeStamp = new Date(Date.now() - 86400000).toISOString(), finishedTimeStamp = new Date(Date.now() - 86400000).toISOString(),
...@@ -66,3 +73,39 @@ export default function mockPipelineData( ...@@ -66,3 +73,39 @@ export default function mockPipelineData(
}, },
}; };
} }
export function mockProjectData(
projectCount = 1,
currentPipelineStatus = 'success',
upstreamStatus = 'success',
alertCount = 0,
) {
return Array(projectCount)
.fill(null)
.map((_, index) => ({
id: index,
description: '',
name: 'test-project',
name_with_namespace: 'Test / test-project',
path: 'test-project',
path_with_namespace: 'test/test-project',
created_at: '2019-02-01T15:40:27.522Z',
default_branch: 'master',
tag_list: [],
avatar_url: null,
web_url: 'https://mock-web_url/',
namespace: {
id: 1,
name: 'test',
path: 'test',
kind: 'user',
full_path: 'user',
parent_id: null,
},
remove_path: '/-/operations?project_id=1',
last_pipeline: mockPipelineData(currentPipelineStatus),
upstream_pipeline: mockPipelineData(upstreamStatus),
downstream_pipelines: [],
alert_count: alertCount,
}));
}
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import store from 'ee/operations/store/index'; import createStore from 'ee/vue_shared/dashboards/store/index';
import * as types from 'ee/operations/store/mutation_types'; import * as types from 'ee/vue_shared/dashboards/store/mutation_types';
import defaultActions, * as actions from 'ee/operations/store/actions'; import defaultActions, * as actions from 'ee/vue_shared/dashboards/store/actions';
import testAction from 'spec/helpers/vuex_action_helper'; import testAction from 'spec/helpers/vuex_action_helper';
import { clearState } from '../helpers'; import clearState from '../helpers';
import { mockText, mockProjectData } from '../mock_data'; import { mockText, mockProjectData } from '../mock_data';
describe('actions', () => { describe('actions', () => {
...@@ -13,9 +13,11 @@ describe('actions', () => { ...@@ -13,9 +13,11 @@ describe('actions', () => {
const mockResponse = { data: 'mock-data' }; const mockResponse = { data: 'mock-data' };
const mockProjects = mockProjectData(1); const mockProjects = mockProjectData(1);
const [mockOneProject] = mockProjects; const [mockOneProject] = mockProjects;
let store;
let mockAxios; let mockAxios;
beforeEach(() => { beforeEach(() => {
store = createStore();
mockAxios = new MockAdapter(axios); mockAxios = new MockAdapter(axios);
}); });
...@@ -118,7 +120,7 @@ describe('actions', () => { ...@@ -118,7 +120,7 @@ describe('actions', () => {
}); });
const errorMessage = const errorMessage =
'The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan.'; 'This dashboard is available for public projects, and private projects in groups with a Gold plan.';
const selectProjects = count => { const selectProjects = count => {
for (let i = 0; i < count; i += 1) { for (let i = 0; i < count; i += 1) {
store.dispatch('toggleSelectedProject', { store.dispatch('toggleSelectedProject', {
...@@ -303,7 +305,7 @@ describe('actions', () => { ...@@ -303,7 +305,7 @@ describe('actions', () => {
}); });
describe('receiveRemoveProjectSuccess', () => { describe('receiveRemoveProjectSuccess', () => {
it('fetches operations dashboard projects', done => { it('fetches dashboard projects', done => {
testAction( testAction(
actions.receiveRemoveProjectSuccess, actions.receiveRemoveProjectSuccess,
null, null,
......
import state from 'ee/operations/store/state'; import state from 'ee/vue_shared/dashboards/store/state';
import mutations from 'ee/operations/store/mutations'; import mutations from 'ee/vue_shared/dashboards/store/mutations';
import * as types from 'ee/operations/store/mutation_types'; import * as types from 'ee/vue_shared/dashboards/store/mutation_types';
import { mockProjectData } from '../mock_data'; import { mockProjectData } from '../mock_data';
describe('mutations', () => { describe('mutations', () => {
......
...@@ -3705,6 +3705,15 @@ msgstr "" ...@@ -3705,6 +3705,15 @@ msgstr ""
msgid "Dashboards" msgid "Dashboards"
msgstr "" msgstr ""
msgid "Dashboard|%{firstProject} and %{secondProject}"
msgstr ""
msgid "Dashboard|%{firstProject}, %{rest}, and %{secondProject}"
msgstr ""
msgid "Dashboard|Unable to add %{invalidProjects}. This dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
msgid "Data is still calculating..." msgid "Data is still calculating..."
msgstr "" msgstr ""
...@@ -8542,9 +8551,6 @@ msgstr "" ...@@ -8542,9 +8551,6 @@ msgstr ""
msgid "OperationsDashboard|The operations dashboard provides a summary of each project's operational health, including pipeline and alert statuses." msgid "OperationsDashboard|The operations dashboard provides a summary of each project's operational health, including pipeline and alert statuses."
msgstr "" msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
msgid "Optional" msgid "Optional"
msgstr "" msgstr ""
...@@ -11309,7 +11315,7 @@ msgstr "" ...@@ -11309,7 +11315,7 @@ 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 get operations projects" msgid "Something went wrong, unable to get projects"
msgstr "" msgstr ""
msgid "Something went wrong, unable to remove project" msgid "Something went wrong, unable to remove project"
......
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