Commit 4a3dec29 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '337274-jira-connect-better-empty-states' into 'master'

Jira Connect create branch page: Alert when lacking permissions

See merge request gitlab-org/gitlab!80043
parents 2993cbf7 469676b5
<script> <script>
import { GlFormGroup, GlButton, GlFormInput, GlForm, GlAlert } from '@gitlab/ui'; import { GlFormGroup, GlButton, GlFormInput, GlForm, GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { import {
CREATE_BRANCH_ERROR_GENERIC, CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT, CREATE_BRANCH_ERROR_WITH_CONTEXT,
...@@ -7,6 +8,7 @@ import { ...@@ -7,6 +8,7 @@ import {
I18N_NEW_BRANCH_LABEL_BRANCH, I18N_NEW_BRANCH_LABEL_BRANCH,
I18N_NEW_BRANCH_LABEL_SOURCE, I18N_NEW_BRANCH_LABEL_SOURCE,
I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT, I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT,
I18N_NEW_BRANCH_PERMISSION_ALERT,
} from '../constants'; } from '../constants';
import createBranchMutation from '../graphql/mutations/create_branch.mutation.graphql'; import createBranchMutation from '../graphql/mutations/create_branch.mutation.graphql';
import ProjectDropdown from './project_dropdown.vue'; import ProjectDropdown from './project_dropdown.vue';
...@@ -17,6 +19,8 @@ const DEFAULT_ALERT_PARAMS = { ...@@ -17,6 +19,8 @@ const DEFAULT_ALERT_PARAMS = {
title: '', title: '',
message: '', message: '',
variant: DEFAULT_ALERT_VARIANT, variant: DEFAULT_ALERT_VARIANT,
link: undefined,
dismissible: true,
}; };
export default { export default {
...@@ -27,10 +31,16 @@ export default { ...@@ -27,10 +31,16 @@ export default {
GlFormInput, GlFormInput,
GlForm, GlForm,
GlAlert, GlAlert,
GlSprintf,
GlLink,
ProjectDropdown, ProjectDropdown,
SourceBranchDropdown, SourceBranchDropdown,
}, },
inject: ['initialBranchName'], inject: {
initialBranchName: {
default: '',
},
},
data() { data() {
return { return {
selectedProject: null, selectedProject: null,
...@@ -40,6 +50,7 @@ export default { ...@@ -40,6 +50,7 @@ export default {
alertParams: { alertParams: {
...DEFAULT_ALERT_PARAMS, ...DEFAULT_ALERT_PARAMS,
}, },
hasPermission: false,
}; };
}, },
computed: { computed: {
...@@ -49,19 +60,38 @@ export default { ...@@ -49,19 +60,38 @@ export default {
showAlert() { showAlert() {
return Boolean(this.alertParams?.message); return Boolean(this.alertParams?.message);
}, },
isBranchNameValid() {
return (this.branchName ?? '').trim().length > 0;
},
disableSubmitButton() { disableSubmitButton() {
return !(this.selectedProject && this.selectedSourceBranchName && this.branchName); return !(this.selectedProject && this.selectedSourceBranchName && this.isBranchNameValid);
}, },
}, },
methods: { methods: {
displayAlert({ title, message, variant = DEFAULT_ALERT_VARIANT } = {}) { displayAlert({
title,
message,
variant = DEFAULT_ALERT_VARIANT,
link,
dismissible = true,
} = {}) {
this.alertParams = { this.alertParams = {
title, title,
message, message,
variant, variant,
link,
dismissible,
}; };
}, },
onAlertDismiss() { setPermissionAlert() {
this.displayAlert({
message: I18N_NEW_BRANCH_PERMISSION_ALERT,
variant: 'warning',
link: helpPagePath('user/permissions', { anchor: 'project-members-permissions' }),
dismissible: false,
});
},
dismissAlert() {
this.alertParams = { this.alertParams = {
...DEFAULT_ALERT_PARAMS, ...DEFAULT_ALERT_PARAMS,
}; };
...@@ -69,6 +99,14 @@ export default { ...@@ -69,6 +99,14 @@ export default {
onProjectSelect(project) { onProjectSelect(project) {
this.selectedProject = project; this.selectedProject = project;
this.selectedSourceBranchName = null; // reset branch selection this.selectedSourceBranchName = null; // reset branch selection
this.hasPermission = this.selectedProject.userPermissions.pushCode;
if (!this.hasPermission) {
this.setPermissionAlert();
} else {
// clear alert if the user has permissions for the newly-selected project.
this.dismissAlert();
}
}, },
onSourceBranchSelect(branchName) { onSourceBranchSelect(branchName) {
this.selectedSourceBranchName = branchName; this.selectedSourceBranchName = branchName;
...@@ -127,10 +165,18 @@ export default { ...@@ -127,10 +165,18 @@ export default {
class="gl-mb-5" class="gl-mb-5"
:variant="alertParams.variant" :variant="alertParams.variant"
:title="alertParams.title" :title="alertParams.title"
@dismiss="onAlertDismiss" :dismissible="alertParams.dismissible"
@dismiss="dismissAlert"
> >
{{ alertParams.message }} <gl-sprintf :message="alertParams.message">
<template #link="{ content }">
<gl-link :href="alertParams.link" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert> </gl-alert>
<gl-form-group :label="$options.i18n.I18N_NEW_BRANCH_LABEL_DROPDOWN" label-for="project-select"> <gl-form-group :label="$options.i18n.I18N_NEW_BRANCH_LABEL_DROPDOWN" label-for="project-select">
<project-dropdown <project-dropdown
id="project-select" id="project-select"
...@@ -140,6 +186,7 @@ export default { ...@@ -140,6 +186,7 @@ export default {
/> />
</gl-form-group> </gl-form-group>
<template v-if="selectedProject && hasPermission">
<gl-form-group <gl-form-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE" :label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE"
label-for="source-branch-select" label-for="source-branch-select"
...@@ -160,6 +207,7 @@ export default { ...@@ -160,6 +207,7 @@ export default {
> >
<gl-form-input id="branch-name-input" v-model="branchName" type="text" required /> <gl-form-input id="branch-name-input" v-model="branchName" type="text" required />
</gl-form-group> </gl-form-group>
</template>
<div class="form-actions"> <div class="form-actions">
<gl-button <gl-button
......
...@@ -23,3 +23,6 @@ export const I18N_NEW_BRANCH_SUCCESS_TITLE = s__( ...@@ -23,3 +23,6 @@ export const I18N_NEW_BRANCH_SUCCESS_TITLE = s__(
export const I18N_NEW_BRANCH_SUCCESS_MESSAGE = s__( export const I18N_NEW_BRANCH_SUCCESS_MESSAGE = s__(
'JiraConnect|You can now close this window and return to Jira.', 'JiraConnect|You can now close this window and return to Jira.',
); );
export const I18N_NEW_BRANCH_PERMISSION_ALERT = s__(
"JiraConnect|You don't have permission to create branches for this project. Select a different project or contact the project owner for access. %{linkStart}Learn more.%{linkEnd}",
);
...@@ -26,6 +26,9 @@ query jiraGetProjects( ...@@ -26,6 +26,9 @@ query jiraGetProjects(
repository { repository {
empty empty
} }
userPermissions {
pushCode
}
} }
pageInfo { pageInfo {
...PageInfo ...PageInfo
......
...@@ -20515,6 +20515,9 @@ msgstr "" ...@@ -20515,6 +20515,9 @@ msgstr ""
msgid "JiraConnect|You can now close this window and return to Jira." msgid "JiraConnect|You can now close this window and return to Jira."
msgstr "" msgstr ""
msgid "JiraConnect|You don't have permission to create branches for this project. Select a different project or contact the project owner for access. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "JiraRequest|A connection error occurred while connecting to Jira. Try your request again." msgid "JiraRequest|A connection error occurred while connecting to Jira. Try your request again."
msgstr "" msgstr ""
......
...@@ -25,8 +25,9 @@ RSpec.describe 'Create GitLab branches from Jira', :js do ...@@ -25,8 +25,9 @@ RSpec.describe 'Create GitLab branches from Jira', :js do
it 'select project and branch and submit the form' do it 'select project and branch and submit the form' do
visit new_jira_connect_branch_path(issue_key: 'ACME-123', issue_summary: 'My issue !@#$% title') visit new_jira_connect_branch_path(issue_key: 'ACME-123', issue_summary: 'My issue !@#$% title')
expect(page).to have_field('Branch name', with: 'ACME-123-my-issue-title')
expect(page).to have_button('Create branch', disabled: true) expect(page).to have_button('Create branch', disabled: true)
# initially, branch field should be hidden.
expect(page).not_to have_field('Branch name')
# Select project1 # Select project1
...@@ -44,6 +45,7 @@ RSpec.describe 'Create GitLab branches from Jira', :js do ...@@ -44,6 +45,7 @@ RSpec.describe 'Create GitLab branches from Jira', :js do
click_on 'Alice / foo' click_on 'Alice / foo'
end end
expect(page).to have_field('Branch name', with: 'ACME-123-my-issue-title')
expect(page).to have_button('Create branch', disabled: false) expect(page).to have_button('Create branch', disabled: false)
click_on 'master' click_on 'master'
......
import { GlAlert, GlForm, GlFormInput, GlButton } from '@gitlab/ui'; import { GlAlert, GlForm, GlFormInput, GlButton, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
...@@ -10,17 +10,12 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran ...@@ -10,17 +10,12 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran
import { import {
CREATE_BRANCH_ERROR_GENERIC, CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT, CREATE_BRANCH_ERROR_WITH_CONTEXT,
I18N_NEW_BRANCH_PERMISSION_ALERT,
} from '~/jira_connect/branches/constants'; } from '~/jira_connect/branches/constants';
import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql'; import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql';
import { mockProjects } from '../mock_data';
const mockProject = { const mockProject = mockProjects[0];
id: 'test',
fullPath: 'test-path',
repository: {
branchNames: ['main', 'f-test', 'release'],
rootRef: 'main',
},
};
const mockCreateBranchMutationResponse = { const mockCreateBranchMutationResponse = {
data: { data: {
createBranch: { createBranch: {
...@@ -52,14 +47,15 @@ describe('NewBranchForm', () => { ...@@ -52,14 +47,15 @@ describe('NewBranchForm', () => {
const findSourceBranchDropdown = () => wrapper.findComponent(SourceBranchDropdown); const findSourceBranchDropdown = () => wrapper.findComponent(SourceBranchDropdown);
const findProjectDropdown = () => wrapper.findComponent(ProjectDropdown); const findProjectDropdown = () => wrapper.findComponent(ProjectDropdown);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
const findAlertSprintf = () => findAlert().findComponent(GlSprintf);
const findForm = () => wrapper.findComponent(GlForm); const findForm = () => wrapper.findComponent(GlForm);
const findInput = () => wrapper.findComponent(GlFormInput); const findInput = () => wrapper.findComponent(GlFormInput);
const findButton = () => wrapper.findComponent(GlButton); const findButton = () => wrapper.findComponent(GlButton);
const completeForm = async () => { const completeForm = async () => {
await findInput().vm.$emit('input', 'cool-branch-name');
await findProjectDropdown().vm.$emit('change', mockProject); await findProjectDropdown().vm.$emit('change', mockProject);
await findSourceBranchDropdown().vm.$emit('change', 'source-branch'); await findSourceBranchDropdown().vm.$emit('change', 'source-branch');
await findInput().vm.$emit('input', 'cool-branch-name');
}; };
function createMockApolloProvider({ function createMockApolloProvider({
...@@ -87,21 +83,58 @@ describe('NewBranchForm', () => { ...@@ -87,21 +83,58 @@ describe('NewBranchForm', () => {
}); });
describe('when selecting items from dropdowns', () => { describe('when selecting items from dropdowns', () => {
describe('when a project is selected', () => { describe('when no project selected', () => {
it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', async () => { beforeEach(() => {
createComponent(); createComponent();
});
const projectDropdown = findProjectDropdown(); it('hides source branch selection and branch name input', () => {
await projectDropdown.vm.$emit('change', mockProject); expect(findSourceBranchDropdown().exists()).toBe(false);
expect(findInput().exists()).toBe(false);
});
it('disables the submit button', () => {
expect(findButton().props('disabled')).toBe(true);
});
});
describe('when a valid project is selected', () => {
describe("when a source branch isn't selected", () => {
beforeEach(async () => {
createComponent();
await findProjectDropdown().vm.$emit('change', mockProject);
});
expect(projectDropdown.props('selectedProject')).toEqual(mockProject); it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', () => {
expect(findProjectDropdown().props('selectedProject')).toEqual(mockProject);
expect(findSourceBranchDropdown().exists()).toBe(true);
expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject); expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject);
}); });
it('disables the submit button', () => {
expect(findButton().props('disabled')).toBe(true);
});
it('renders branch input field', () => {
expect(findInput().exists()).toBe(true);
});
});
describe('when `initialBranchName` is provided', () => {
it('sets value of branch name input to `initialBranchName` by default', async () => {
const mockInitialBranchName = 'ap1-test-branch-name';
createComponent({ provide: { initialBranchName: mockInitialBranchName } });
await findProjectDropdown().vm.$emit('change', mockProject);
expect(findInput().attributes('value')).toBe(mockInitialBranchName);
});
}); });
describe('when a source branch is selected', () => { describe('when a source branch is selected', () => {
it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => { it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => {
createComponent(); createComponent();
await completeForm();
const mockBranchName = 'main'; const mockBranchName = 'main';
const sourceBranchDropdown = findSourceBranchDropdown(); const sourceBranchDropdown = findSourceBranchDropdown();
...@@ -109,6 +142,49 @@ describe('NewBranchForm', () => { ...@@ -109,6 +142,49 @@ describe('NewBranchForm', () => {
expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName); expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName);
}); });
describe.each`
branchName | submitButtonDisabled
${undefined} | ${true}
${''} | ${true}
${' '} | ${true}
${'test-branch'} | ${false}
`('when branch name is $branchName', ({ branchName, submitButtonDisabled }) => {
it(`sets submit button 'disabled' prop to ${submitButtonDisabled}`, async () => {
createComponent();
await completeForm();
await findInput().vm.$emit('input', branchName);
expect(findButton().props('disabled')).toBe(submitButtonDisabled);
});
});
});
});
describe("when user doesn't have push permissions for the selected project", () => {
beforeEach(async () => {
createComponent();
const projectDropdown = findProjectDropdown();
await projectDropdown.vm.$emit('change', {
...mockProject,
userPermissions: { pushCode: false },
});
});
it('displays an alert', () => {
const alert = findAlert();
expect(alert.exists()).toBe(true);
expect(findAlertSprintf().attributes('message')).toBe(I18N_NEW_BRANCH_PERMISSION_ALERT);
expect(alert.props('variant')).toBe('warning');
expect(alert.props('dismissible')).toBe(false);
});
it('hides source branch selection and branch name input', () => {
expect(findSourceBranchDropdown().exists()).toBe(false);
expect(findInput().exists()).toBe(false);
});
}); });
}); });
...@@ -179,7 +255,7 @@ describe('NewBranchForm', () => { ...@@ -179,7 +255,7 @@ describe('NewBranchForm', () => {
it('displays an alert', () => { it('displays an alert', () => {
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
expect(alert.text()).toBe(alertText); expect(findAlertSprintf().attributes('message')).toBe(alertText);
expect(alert.props()).toMatchObject({ title: alertTitle, variant: 'danger' }); expect(alert.props()).toMatchObject({ title: alertTitle, variant: 'danger' });
}); });
...@@ -190,15 +266,6 @@ describe('NewBranchForm', () => { ...@@ -190,15 +266,6 @@ describe('NewBranchForm', () => {
}); });
}); });
describe('when `initialBranchName` is specified', () => {
it('sets value of branch name input to `initialBranchName` by default', () => {
const mockInitialBranchName = 'ap1-test-branch-name';
createComponent({ provide: { initialBranchName: mockInitialBranchName } });
expect(findInput().attributes('value')).toBe(mockInitialBranchName);
});
});
describe('error handling', () => { describe('error handling', () => {
describe.each` describe.each`
component | componentName component | componentName
...@@ -209,13 +276,15 @@ describe('NewBranchForm', () => { ...@@ -209,13 +276,15 @@ describe('NewBranchForm', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent(); createComponent();
await completeForm();
await wrapper.findComponent(component).vm.$emit('error', { message: mockErrorMessage }); await wrapper.findComponent(component).vm.$emit('error', { message: mockErrorMessage });
}); });
it('displays an alert', () => { it('displays an alert', () => {
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
expect(alert.text()).toBe(mockErrorMessage); expect(findAlertSprintf().attributes('message')).toBe(mockErrorMessage);
expect(alert.props('variant')).toBe('danger'); expect(alert.props('variant')).toBe('danger');
}); });
......
...@@ -14,30 +14,7 @@ import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown ...@@ -14,30 +14,7 @@ import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown
import { PROJECTS_PER_PAGE } from '~/jira_connect/branches/constants'; import { PROJECTS_PER_PAGE } from '~/jira_connect/branches/constants';
import getProjectsQuery from '~/jira_connect/branches/graphql/queries/get_projects.query.graphql'; import getProjectsQuery from '~/jira_connect/branches/graphql/queries/get_projects.query.graphql';
const mockProjects = [ import { mockProjects } from '../mock_data';
{
id: 'test',
name: 'test',
nameWithNamespace: 'test',
avatarUrl: 'https://gitlab.com',
path: 'test-path',
fullPath: 'test-path',
repository: {
empty: false,
},
},
{
id: 'gitlab',
name: 'GitLab',
nameWithNamespace: 'gitlab-org/gitlab',
avatarUrl: 'https://gitlab.com',
path: 'gitlab',
fullPath: 'gitlab-org/gitlab',
repository: {
empty: false,
},
},
];
const mockProjectsQueryResponse = { const mockProjectsQueryResponse = {
data: { data: {
...@@ -134,7 +111,7 @@ describe('ProjectDropdown', () => { ...@@ -134,7 +111,7 @@ describe('ProjectDropdown', () => {
}); });
describe('when selecting a dropdown item', () => { describe('when selecting a dropdown item', () => {
it('emits `change` event with the selected project name', async () => { it('emits `change` event with the selected project', async () => {
const mockProject = mockProjects[0]; const mockProject = mockProjects[0];
const itemToSelect = findDropdownItemByProjectId(mockProject.id); const itemToSelect = findDropdownItemByProjectId(mockProject.id);
await itemToSelect.vm.$emit('click'); await itemToSelect.vm.$emit('click');
...@@ -146,7 +123,7 @@ describe('ProjectDropdown', () => { ...@@ -146,7 +123,7 @@ describe('ProjectDropdown', () => {
describe('when `selectedProject` prop is specified', () => { describe('when `selectedProject` prop is specified', () => {
const mockProject = mockProjects[0]; const mockProject = mockProjects[0];
beforeEach(async () => { beforeEach(() => {
wrapper.setProps({ wrapper.setProps({
selectedProject: mockProject, selectedProject: mockProject,
}); });
......
...@@ -7,15 +7,16 @@ import waitForPromises from 'helpers/wait_for_promises'; ...@@ -7,15 +7,16 @@ import waitForPromises from 'helpers/wait_for_promises';
import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue'; import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue';
import { BRANCHES_PER_PAGE } from '~/jira_connect/branches/constants'; import { BRANCHES_PER_PAGE } from '~/jira_connect/branches/constants';
import getProjectQuery from '~/jira_connect/branches/graphql/queries/get_project.query.graphql'; import getProjectQuery from '~/jira_connect/branches/graphql/queries/get_project.query.graphql';
import { mockProjects } from '../mock_data';
const mockProject = { const mockProject = {
id: 'test', id: 'test',
fullPath: 'test-path',
repository: { repository: {
branchNames: ['main', 'f-test', 'release'], branchNames: ['main', 'f-test', 'release'],
rootRef: 'main', rootRef: 'main',
}, },
}; };
const mockSelectedProject = mockProjects[0];
const mockProjectQueryResponse = { const mockProjectQueryResponse = {
data: { data: {
...@@ -76,7 +77,7 @@ describe('SourceBranchDropdown', () => { ...@@ -76,7 +77,7 @@ describe('SourceBranchDropdown', () => {
describe('when `selectedProject` becomes specified', () => { describe('when `selectedProject` becomes specified', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper.setProps({ wrapper.setProps({
selectedProject: mockProject, selectedProject: mockSelectedProject,
}); });
await waitForPromises(); await waitForPromises();
...@@ -101,7 +102,7 @@ describe('SourceBranchDropdown', () => { ...@@ -101,7 +102,7 @@ describe('SourceBranchDropdown', () => {
it('renders loading icon in dropdown', () => { it('renders loading icon in dropdown', () => {
createComponent({ createComponent({
mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }), mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }),
props: { selectedProject: mockProject }, props: { selectedProject: mockSelectedProject },
}); });
expect(findLoadingIcon().isVisible()).toBe(true); expect(findLoadingIcon().isVisible()).toBe(true);
...@@ -111,7 +112,7 @@ describe('SourceBranchDropdown', () => { ...@@ -111,7 +112,7 @@ describe('SourceBranchDropdown', () => {
describe('when branches have loaded', () => { describe('when branches have loaded', () => {
describe('when searching branches', () => { describe('when searching branches', () => {
it('triggers a refetch', async () => { it('triggers a refetch', async () => {
createComponent({ mountFn: mount, props: { selectedProject: mockProject } }); createComponent({ mountFn: mount, props: { selectedProject: mockSelectedProject } });
await waitForPromises(); await waitForPromises();
jest.clearAllMocks(); jest.clearAllMocks();
...@@ -129,7 +130,7 @@ describe('SourceBranchDropdown', () => { ...@@ -129,7 +130,7 @@ describe('SourceBranchDropdown', () => {
describe('template', () => { describe('template', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ props: { selectedProject: mockProject } }); createComponent({ props: { selectedProject: mockSelectedProject } });
await waitForPromises(); await waitForPromises();
}); });
......
export const mockProjects = [
{
id: 'test',
name: 'test',
nameWithNamespace: 'test',
avatarUrl: 'https://gitlab.com',
path: 'test-path',
fullPath: 'test-path',
repository: {
empty: false,
},
userPermissions: {
pushCode: true,
},
},
{
id: 'gitlab',
name: 'GitLab',
nameWithNamespace: 'gitlab-org/gitlab',
avatarUrl: 'https://gitlab.com',
path: 'gitlab',
fullPath: 'gitlab-org/gitlab',
repository: {
empty: false,
},
userPermissions: {
pushCode: true,
},
},
];
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