Commit c163dae4 authored by Coung Ngo's avatar Coung Ngo Committed by Kushal Pandya

Add ability to initiate import of Jira project

Added ability to initiate import of Jira project
in a project on the frontend
parent 93d29d34
<script> <script>
import getJiraProjects from '../queries/getJiraProjects.query.graphql'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import getJiraImportDetailsQuery from '../queries/get_jira_import_details.query.graphql';
import initiateJiraImportMutation from '../queries/initiate_jira_import.mutation.graphql';
import { IMPORT_STATE, isInProgress } from '../utils';
import JiraImportForm from './jira_import_form.vue'; import JiraImportForm from './jira_import_form.vue';
import JiraImportProgress from './jira_import_progress.vue';
import JiraImportSetup from './jira_import_setup.vue'; import JiraImportSetup from './jira_import_setup.vue';
export default { export default {
name: 'JiraImportApp', name: 'JiraImportApp',
components: { components: {
GlAlert,
GlLoadingIcon,
JiraImportForm, JiraImportForm,
JiraImportProgress,
JiraImportSetup, JiraImportSetup,
}, },
props: { props: {
...@@ -14,6 +22,18 @@ export default { ...@@ -14,6 +22,18 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
inProgressIllustration: {
type: String,
required: true,
},
issuesPath: {
type: String,
required: true,
},
jiraProjects: {
type: Array,
required: true,
},
projectPath: { projectPath: {
type: String, type: String,
required: true, required: true,
...@@ -23,26 +43,111 @@ export default { ...@@ -23,26 +43,111 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
errorMessage: '',
showAlert: false,
};
},
apollo: { apollo: {
getJiraImports: { jiraImportDetails: {
query: getJiraProjects, query: getJiraImportDetailsQuery,
variables() { variables() {
return { return {
fullPath: this.projectPath, fullPath: this.projectPath,
}; };
}, },
update: data => data.project.jiraImports, update: ({ project }) => ({
status: project.jiraImportStatus,
import: project.jiraImports.nodes[0],
}),
skip() { skip() {
return !this.isJiraConfigured; return !this.isJiraConfigured;
}, },
}, },
}, },
computed: {
isImportInProgress() {
return isInProgress(this.jiraImportDetails?.status);
},
jiraProjectsOptions() {
return this.jiraProjects.map(([text, value]) => ({ text, value }));
},
},
methods: {
dismissAlert() {
this.showAlert = false;
},
initiateJiraImport(project) {
this.$apollo
.mutate({
mutation: initiateJiraImportMutation,
variables: {
input: {
projectPath: this.projectPath,
jiraProjectKey: project,
},
},
update: (store, { data }) => {
if (data.jiraImportStart.errors.length) {
return;
}
store.writeQuery({
query: getJiraImportDetailsQuery,
variables: {
fullPath: this.projectPath,
},
data: {
project: {
jiraImportStatus: IMPORT_STATE.SCHEDULED,
jiraImports: {
nodes: [data.jiraImportStart.jiraImport],
__typename: 'JiraImportConnection',
},
// eslint-disable-next-line @gitlab/require-i18n-strings
__typename: 'Project',
},
},
});
},
})
.then(({ data }) => {
if (data.jiraImportStart.errors.length) {
this.setAlertMessage(data.jiraImportStart.errors.join('. '));
}
})
.catch(() => this.setAlertMessage(__('There was an error importing the Jira project.')));
},
setAlertMessage(message) {
this.errorMessage = message;
this.showAlert = true;
},
},
}; };
</script> </script>
<template> <template>
<div> <div>
<gl-alert v-if="showAlert" variant="danger" @dismiss="dismissAlert">
{{ errorMessage }}
</gl-alert>
<jira-import-setup v-if="!isJiraConfigured" :illustration="setupIllustration" /> <jira-import-setup v-if="!isJiraConfigured" :illustration="setupIllustration" />
<jira-import-form v-else /> <gl-loading-icon v-else-if="$apollo.loading" size="md" class="mt-3" />
<jira-import-progress
v-else-if="isImportInProgress"
:illustration="inProgressIllustration"
:import-initiator="jiraImportDetails.import.scheduledBy.name"
:import-project="jiraImportDetails.import.jiraProjectKey"
:import-time="jiraImportDetails.import.scheduledAt"
:issues-path="issuesPath"
/>
<jira-import-form
v-else
:issues-path="issuesPath"
:jira-projects="jiraProjectsOptions"
@initiateJiraImport="initiateJiraImport"
/>
</div> </div>
</template> </template>
...@@ -12,6 +12,39 @@ export default { ...@@ -12,6 +12,39 @@ export default {
}, },
currentUserAvatarUrl: gon.current_user_avatar_url, currentUserAvatarUrl: gon.current_user_avatar_url,
currentUsername: gon.current_username, currentUsername: gon.current_username,
props: {
issuesPath: {
type: String,
required: true,
},
jiraProjects: {
type: Array,
required: true,
},
},
data() {
return {
selectedOption: null,
selectState: null,
};
},
methods: {
initiateJiraImport(event) {
event.preventDefault();
if (!this.selectedOption) {
this.showValidationError();
} else {
this.hideValidationError();
this.$emit('initiateJiraImport', this.selectedOption);
}
},
hideValidationError() {
this.selectState = null;
},
showValidationError() {
this.selectState = false;
},
},
}; };
</script> </script>
...@@ -19,14 +52,21 @@ export default { ...@@ -19,14 +52,21 @@ export default {
<div> <div>
<h3 class="page-title">{{ __('New Jira import') }}</h3> <h3 class="page-title">{{ __('New Jira import') }}</h3>
<hr /> <hr />
<form> <form @submit="initiateJiraImport">
<gl-form-group <gl-form-group
class="row align-items-center" class="row align-items-center"
:invalid-feedback="__('Please select a Jira project')"
:label="__('Import from')" :label="__('Import from')"
label-cols-sm="2" label-cols-sm="2"
label-for="jira-project-select" label-for="jira-project-select"
> >
<gl-form-select id="jira-project-select" class="mb-2" /> <gl-form-select
id="jira-project-select"
v-model="selectedOption"
class="mb-2"
:options="jiraProjects"
:state="selectState"
/>
</gl-form-group> </gl-form-group>
<gl-form-group <gl-form-group
...@@ -86,8 +126,10 @@ export default { ...@@ -86,8 +126,10 @@ export default {
</gl-form-group> </gl-form-group>
<div class="footer-block row-content-block d-flex justify-content-between"> <div class="footer-block row-content-block d-flex justify-content-between">
<gl-button category="primary" variant="success">{{ __('Next') }}</gl-button> <gl-button type="submit" category="primary" variant="success" class="js-no-auto-disable">
<gl-button>{{ __('Cancel') }}</gl-button> {{ __('Next') }}
</gl-button>
<gl-button :href="issuesPath">{{ __('Cancel') }}</gl-button>
</div> </div>
</form> </form>
</div> </div>
......
<script>
import { GlEmptyState } from '@gitlab/ui';
import { formatDate } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
export default {
name: 'JiraImportProgress',
components: {
GlEmptyState,
},
props: {
illustration: {
type: String,
required: true,
},
importInitiator: {
type: String,
required: true,
},
importProject: {
type: String,
required: true,
},
importTime: {
type: String,
required: true,
},
issuesPath: {
type: String,
required: true,
},
},
computed: {
importInitiatorText() {
return sprintf(__('Import started by: %{importInitiator}'), {
importInitiator: this.importInitiator,
});
},
importProjectText() {
return sprintf(__('Jira project: %{importProject}'), {
importProject: this.importProject,
});
},
importTimeText() {
return sprintf(__('Time of import: %{importTime}'), {
importTime: formatDate(this.importTime),
});
},
},
};
</script>
<template>
<gl-empty-state
:svg-path="illustration"
:title="__('Import in progress')"
:primary-button-text="__('View issues')"
:primary-button-link="issuesPath"
>
<template #description>
<p class="mb-0">{{ importInitiatorText }}</p>
<p class="mb-0">{{ importTimeText }}</p>
<p class="mb-0">{{ importProjectText }}</p>
</template>
</gl-empty-state>
</template>
<script> <script>
import { GlEmptyState } from '@gitlab/ui';
export default { export default {
name: 'JiraImportSetup', name: 'JiraImportSetup',
components: {
GlEmptyState,
},
props: { props: {
illustration: { illustration: {
type: String, type: String,
...@@ -11,15 +16,11 @@ export default { ...@@ -11,15 +16,11 @@ export default {
</script> </script>
<template> <template>
<div class="empty-state"> <gl-empty-state
<div class="svg-content"> :svg-path="illustration"
<img :src="illustration" :alt="__('Set up Jira Integration illustration')" /> title=""
</div> :description="__('You will first need to set up Jira Integration to use this feature.')"
<div class="text-content d-flex flex-column align-items-center"> :primary-button-text="__('Set up Jira Integration')"
<p>{{ __('You will first need to set up Jira Integration to use this feature.') }}</p> primary-button-link="../services/jira/edit"
<a class="btn btn-success" href="../services/jira/edit"> />
{{ __('Set up Jira Integration') }}
</a>
</div>
</div>
</template> </template>
...@@ -24,7 +24,10 @@ export default function mountJiraImportApp() { ...@@ -24,7 +24,10 @@ export default function mountJiraImportApp() {
render(createComponent) { render(createComponent) {
return createComponent(App, { return createComponent(App, {
props: { props: {
inProgressIllustration: el.dataset.inProgressIllustration,
isJiraConfigured: parseBoolean(el.dataset.isJiraConfigured), isJiraConfigured: parseBoolean(el.dataset.isJiraConfigured),
issuesPath: el.dataset.issuesPath,
jiraProjects: el.dataset.jiraProjects ? JSON.parse(el.dataset.jiraProjects) : [],
projectPath: el.dataset.projectPath, projectPath: el.dataset.projectPath,
setupIllustration: el.dataset.setupIllustration, setupIllustration: el.dataset.setupIllustration,
}, },
......
query getJiraProjects($fullPath: ID!) { #import "./jira_import.fragment.graphql"
query($fullPath: ID!) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
jiraImportStatus jiraImportStatus
jiraImports { jiraImports(last: 1) {
nodes { nodes {
jiraProjectKey ...JiraImport
scheduledAt
scheduledBy {
username
}
} }
} }
} }
......
#import "./jira_import.fragment.graphql"
mutation($input: JiraImportStartInput!) {
jiraImportStart(input: $input) {
clientMutationId
jiraImport {
...JiraImport
}
errors
}
}
fragment JiraImport on JiraImport {
jiraProjectKey
scheduledAt
scheduledBy {
name
}
}
export const IMPORT_STATE = {
FAILED: 'failed',
FINISHED: 'finished',
NONE: 'none',
SCHEDULED: 'scheduled',
STARTED: 'started',
};
export const isInProgress = state =>
state === IMPORT_STATE.SCHEDULED || state === IMPORT_STATE.STARTED;
...@@ -11,11 +11,10 @@ module Projects ...@@ -11,11 +11,10 @@ module Projects
before_action :authorize_admin_project!, only: [:import] before_action :authorize_admin_project!, only: [:import]
def show def show
@is_jira_configured = @project.jira_service.present? jira_service = @project.jira_service
return if Feature.enabled?(:jira_issue_import_vue, @project)
if !@project.latest_jira_import&.in_progress? && current_user&.can?(:admin_project, @project) if jira_service.present? && !@project.latest_jira_import&.in_progress? && current_user&.can?(:admin_project, @project)
jira_client = @project.jira_service.client jira_client = jira_service.client
jira_projects = jira_client.Project.all jira_projects = jira_client.Project.all
if jira_projects.present? if jira_projects.present?
...@@ -25,8 +24,10 @@ module Projects ...@@ -25,8 +24,10 @@ module Projects
end end
end end
unless Feature.enabled?(:jira_issue_import_vue, @project)
flash[:notice] = _("Import %{status}") % { status: @project.jira_import_status } unless @project.latest_jira_import&.initial? flash[:notice] = _("Import %{status}") % { status: @project.jira_import_status } unless @project.latest_jira_import&.initial?
end end
end
def import def import
jira_project_key = jira_import_params[:jira_project_key] jira_project_key = jira_import_params[:jira_project_key]
......
- if Feature.enabled?(:jira_issue_import_vue, @project) - if Feature.enabled?(:jira_issue_import_vue, @project)
.js-jira-import-root{ data: { project_path: @project.full_path, .js-jira-import-root{ data: { project_path: @project.full_path,
is_jira_configured: @is_jira_configured.to_s, issues_path: project_issues_path(@project),
is_jira_configured: @project.jira_service.present?.to_s,
jira_projects: @jira_projects.to_json,
in_progress_illustration: image_path('illustrations/export-import.svg'),
setup_illustration: image_path('illustrations/manual_action.svg') } } setup_illustration: image_path('illustrations/manual_action.svg') } }
- else - else
- title = _('Jira Issue Import') - title = _('Jira Issue Import')
......
...@@ -11031,6 +11031,9 @@ msgstr "" ...@@ -11031,6 +11031,9 @@ msgstr ""
msgid "Import repository" msgid "Import repository"
msgstr "" msgstr ""
msgid "Import started by: %{importInitiator}"
msgstr ""
msgid "Import tasks" msgid "Import tasks"
msgstr "" msgstr ""
...@@ -11531,6 +11534,9 @@ msgstr "" ...@@ -11531,6 +11534,9 @@ msgstr ""
msgid "Jira integration not configured." msgid "Jira integration not configured."
msgstr "" msgstr ""
msgid "Jira project: %{importProject}"
msgstr ""
msgid "JiraService|Events for %{noteable_model_name} are disabled." msgid "JiraService|Events for %{noteable_model_name} are disabled."
msgstr "" msgstr ""
...@@ -15031,6 +15037,9 @@ msgstr "" ...@@ -15031,6 +15037,9 @@ msgstr ""
msgid "Please select" msgid "Please select"
msgstr "" msgstr ""
msgid "Please select a Jira project"
msgstr ""
msgid "Please select a country" msgid "Please select a country"
msgstr "" msgstr ""
...@@ -18591,9 +18600,6 @@ msgstr "" ...@@ -18591,9 +18600,6 @@ msgstr ""
msgid "Set up Jira Integration" msgid "Set up Jira Integration"
msgstr "" msgstr ""
msgid "Set up Jira Integration illustration"
msgstr ""
msgid "Set up a %{type} Runner automatically" msgid "Set up a %{type} Runner automatically"
msgstr "" msgstr ""
...@@ -20768,6 +20774,9 @@ msgstr "" ...@@ -20768,6 +20774,9 @@ msgstr ""
msgid "There was an error getting the epic participants." msgid "There was an error getting the epic participants."
msgstr "" msgstr ""
msgid "There was an error importing the Jira project."
msgstr ""
msgid "There was an error loading users activity calendar." msgid "There was an error loading users activity calendar."
msgstr "" msgstr ""
...@@ -21359,6 +21368,9 @@ msgstr "" ...@@ -21359,6 +21368,9 @@ msgstr ""
msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied." msgid "Time in seconds GitLab will wait for a response from the external service. When the service does not respond in time, access will be denied."
msgstr "" msgstr ""
msgid "Time of import: %{importTime}"
msgstr ""
msgid "Time remaining" msgid "Time remaining"
msgstr "" msgstr ""
...@@ -22951,6 +22963,9 @@ msgstr "" ...@@ -22951,6 +22963,9 @@ msgstr ""
msgid "View issue" msgid "View issue"
msgstr "" msgstr ""
msgid "View issues"
msgstr ""
msgid "View it on GitLab" msgid "View it on GitLab"
msgstr "" msgstr ""
......
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import JiraImportApp from '~/jira_import/components/jira_import_app.vue'; import JiraImportApp from '~/jira_import/components/jira_import_app.vue';
import JiraImportForm from '~/jira_import/components/jira_import_form.vue';
import JiraImportProgress from '~/jira_import/components/jira_import_progress.vue';
import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue'; import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue';
import initiateJiraImportMutation from '~/jira_import/queries/initiate_jira_import.mutation.graphql';
import { IMPORT_STATE } from '~/jira_import/utils';
const mountComponent = ({
isJiraConfigured = true,
errorMessage = '',
showAlert = true,
status = IMPORT_STATE.NONE,
loading = false,
mutate = jest.fn(() => Promise.resolve()),
} = {}) =>
shallowMount(JiraImportApp, {
propsData: {
isJiraConfigured,
inProgressIllustration: 'in-progress-illustration.svg',
issuesPath: 'gitlab-org/gitlab-test/-/issues',
jiraProjects: [
['My Jira Project', 'MJP'],
['My Second Jira Project', 'MSJP'],
['Migrate to GitLab', 'MTG'],
],
projectPath: 'gitlab-org/gitlab-test',
setupIllustration: 'setup-illustration.svg',
},
data() {
return {
errorMessage,
showAlert,
jiraImportDetails: {
status,
import: {
jiraProjectKey: 'MTG',
scheduledAt: '2020-04-08T12:17:25+00:00',
scheduledBy: {
name: 'Jane Doe',
},
},
},
};
},
mocks: {
$apollo: {
loading,
mutate,
},
},
});
describe('JiraImportApp', () => { describe('JiraImportApp', () => {
let wrapper; let wrapper;
const getFormComponent = () => wrapper.find(JiraImportForm);
const getProgressComponent = () => wrapper.find(JiraImportProgress);
const getSetupComponent = () => wrapper.find(JiraImportSetup);
const getAlert = () => wrapper.find(GlAlert);
const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; wrapper = null;
}); });
describe('set up Jira integration page', () => { describe('when Jira integration is not configured', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(JiraImportApp, { wrapper = mountComponent({ isJiraConfigured: false });
propsData: { });
isJiraConfigured: true,
projectPath: 'gitlab-org/gitlab-test', it('shows the "Set up Jira integration" screen', () => {
setupIllustration: 'illustration.svg', expect(getSetupComponent().exists()).toBe(true);
}, });
it('does not show loading icon', () => {
expect(getLoadingIcon().exists()).toBe(false);
});
it('does not show the "Import in progress" screen', () => {
expect(getProgressComponent().exists()).toBe(false);
});
it('does not show the "Import Jira project" form', () => {
expect(getFormComponent().exists()).toBe(false);
});
});
describe('when Jira integration is configured but data is being fetched', () => {
beforeEach(() => {
wrapper = mountComponent({ loading: true });
});
it('does not show the "Set up Jira integration" screen', () => {
expect(getSetupComponent().exists()).toBe(false);
});
it('shows loading icon', () => {
expect(getLoadingIcon().exists()).toBe(true);
});
it('does not show the "Import in progress" screen', () => {
expect(getProgressComponent().exists()).toBe(false);
});
it('does not show the "Import Jira project" form', () => {
expect(getFormComponent().exists()).toBe(false);
}); });
}); });
it('is shown when Jira integration is not configured', () => { describe('when Jira integration is configured but import is in progress', () => {
wrapper.setProps({ beforeEach(() => {
isJiraConfigured: false, wrapper = mountComponent({ status: IMPORT_STATE.SCHEDULED });
});
it('does not show the "Set up Jira integration" screen', () => {
expect(getSetupComponent().exists()).toBe(false);
});
it('does not show loading icon', () => {
expect(getLoadingIcon().exists()).toBe(false);
});
it('shows the "Import in progress" screen', () => {
expect(getProgressComponent().exists()).toBe(true);
});
it('does not show the "Import Jira project" form', () => {
expect(getFormComponent().exists()).toBe(false);
});
}); });
return wrapper.vm.$nextTick(() => { describe('when Jira integration is configured and there is no import in progress', () => {
expect(wrapper.find(JiraImportSetup).exists()).toBe(true); beforeEach(() => {
wrapper = mountComponent();
});
it('does not show the "Set up Jira integration" screen', () => {
expect(getSetupComponent().exists()).toBe(false);
});
it('does not show loading icon', () => {
expect(getLoadingIcon().exists()).toBe(false);
}); });
it('does not show the Import in progress" screen', () => {
expect(getProgressComponent().exists()).toBe(false);
});
it('shows the "Import Jira project" form', () => {
expect(getFormComponent().exists()).toBe(true);
}); });
});
describe('initiating a Jira import', () => {
it('calls the mutation with the expected arguments', () => {
const mutate = jest.fn(() => Promise.resolve());
wrapper = mountComponent({ mutate });
const mutationArguments = {
mutation: initiateJiraImportMutation,
variables: {
input: {
jiraProjectKey: 'MTG',
projectPath: 'gitlab-org/gitlab-test',
},
},
};
getFormComponent().vm.$emit('initiateJiraImport', 'MTG');
expect(mutate).toHaveBeenCalledWith(expect.objectContaining(mutationArguments));
});
it('shows alert message with error message on error', () => {
const mutate = jest.fn(() => Promise.reject());
wrapper = mountComponent({ mutate });
getFormComponent().vm.$emit('initiateJiraImport', 'MTG');
// One tick doesn't update the dom to the desired state so we have two ticks here
return Vue.nextTick()
.then(Vue.nextTick)
.then(() => {
expect(getAlert().text()).toBe('There was an error importing the Jira project.');
});
});
});
it('can dismiss alert message', () => {
wrapper = mountComponent({
errorMessage: 'There was an error importing the Jira project.',
showAlert: true,
});
expect(getAlert().exists()).toBe(true);
getAlert().vm.$emit('dismiss');
it('is not shown when Jira integration is configured', () => { return Vue.nextTick().then(() => {
expect(wrapper.find(JiraImportSetup).exists()).toBe(false); expect(getAlert().exists()).toBe(false);
}); });
}); });
}); });
import { GlAvatar, GlButton, GlFormSelect, GlLabel } from '@gitlab/ui'; import { GlAvatar, GlButton, GlFormSelect, GlLabel } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import JiraImportForm from '~/jira_import/components/jira_import_form.vue'; import JiraImportForm from '~/jira_import/components/jira_import_form.vue';
const mountComponent = ({ mountType } = {}) => {
const mountFunction = mountType === 'mount' ? mount : shallowMount;
return mountFunction(JiraImportForm, {
propsData: {
issuesPath: 'gitlab-org/gitlab-test/-/issues',
jiraProjects: [
{
text: 'My Jira Project',
value: 'MJP',
},
{
text: 'My Second Jira Project',
value: 'MSJP',
},
{
text: 'Migrate to GitLab',
value: 'MTG',
},
],
},
});
};
describe('JiraImportForm', () => { describe('JiraImportForm', () => {
let wrapper; let wrapper;
beforeEach(() => { const getCancelButton = () => wrapper.findAll(GlButton).at(1);
wrapper = shallowMount(JiraImportForm);
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; wrapper = null;
}); });
it('shows a dropdown to choose the Jira project to import from', () => { describe('select dropdown', () => {
it('is shown', () => {
wrapper = mountComponent();
expect(wrapper.find(GlFormSelect).exists()).toBe(true); expect(wrapper.find(GlFormSelect).exists()).toBe(true);
}); });
it('contains a list of Jira projects to select from', () => {
wrapper = mountComponent({ mountType: 'mount' });
const optionItems = ['My Jira Project', 'My Second Jira Project', 'Migrate to GitLab'];
wrapper
.find(GlFormSelect)
.findAll('option')
.wrappers.forEach((optionEl, index) => {
expect(optionEl.text()).toBe(optionItems[index]);
});
});
});
describe('form information', () => {
beforeEach(() => {
wrapper = mountComponent();
});
it('shows a label which will be applied to imported Jira projects', () => { it('shows a label which will be applied to imported Jira projects', () => {
expect(wrapper.find(GlLabel).attributes('title')).toBe('jira-import::KEY-1'); expect(wrapper.find(GlLabel).attributes('title')).toBe('jira-import::KEY-1');
}); });
...@@ -41,22 +85,42 @@ describe('JiraImportForm', () => { ...@@ -41,22 +85,42 @@ describe('JiraImportForm', () => {
'jira.issue.description.content', 'jira.issue.description.content',
); );
}); });
});
describe('Next button', () => {
beforeEach(() => {
wrapper = mountComponent();
});
it('is shown', () => {
expect(wrapper.find(GlButton).text()).toBe('Next');
});
});
describe('Cancel button', () => {
beforeEach(() => {
wrapper = mountComponent();
});
it('is shown', () => {
expect(getCancelButton().text()).toBe('Cancel');
});
it('links to the Issues page', () => {
expect(getCancelButton().attributes('href')).toBe('gitlab-org/gitlab-test/-/issues');
});
});
it('shows a Next button', () => { it('emits an "initiateJiraImport" event with the selected dropdown value when submitted', () => {
const nextButton = wrapper const selectedOption = 'MTG';
.findAll(GlButton)
.at(0)
.text();
expect(nextButton).toBe('Next'); wrapper = mountComponent();
wrapper.setData({
selectedOption,
}); });
it('shows a Cancel button', () => { wrapper.find('form').trigger('submit');
const cancelButton = wrapper
.findAll(GlButton)
.at(1)
.text();
expect(cancelButton).toBe('Cancel'); expect(wrapper.emitted('initiateJiraImport')[0]).toEqual([selectedOption]);
}); });
}); });
import { GlEmptyState } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import JiraImportProgress from '~/jira_import/components/jira_import_progress.vue';
describe('JiraImportProgress', () => {
let wrapper;
const getGlEmptyStateAttribute = attribute => wrapper.find(GlEmptyState).attributes(attribute);
const getParagraphText = () => wrapper.find('p').text();
const mountComponent = ({ mountType = 'shallowMount' } = {}) => {
const mountFunction = mountType === 'shallowMount' ? shallowMount : mount;
return mountFunction(JiraImportProgress, {
propsData: {
illustration: 'illustration.svg',
importInitiator: 'Jane Doe',
importProject: 'JIRAPROJECT',
importTime: '2020-04-08T12:17:25+00:00',
issuesPath: 'gitlab-org/gitlab-test/-/issues',
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('empty state', () => {
beforeEach(() => {
wrapper = mountComponent();
});
it('contains illustration', () => {
expect(getGlEmptyStateAttribute('svgpath')).toBe('illustration.svg');
});
it('contains a title', () => {
const title = 'Import in progress';
expect(getGlEmptyStateAttribute('title')).toBe(title);
});
it('contains button text', () => {
expect(getGlEmptyStateAttribute('primarybuttontext')).toBe('View issues');
});
it('contains button url', () => {
expect(getGlEmptyStateAttribute('primarybuttonlink')).toBe('gitlab-org/gitlab-test/-/issues');
});
});
describe('description', () => {
beforeEach(() => {
wrapper = mountComponent({ mountType: 'mount' });
});
it('shows who initiated the import', () => {
expect(getParagraphText()).toContain('Import started by: Jane Doe');
});
it('shows the time of import', () => {
expect(getParagraphText()).toContain('Time of import: Apr 8, 2020 12:17pm GMT+0000');
});
it('shows the project key of the import', () => {
expect(getParagraphText()).toContain('Jira project: JIRAPROJECT');
});
});
});
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue'; import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue';
describe('JiraImportSetup', () => { describe('JiraImportSetup', () => {
let wrapper; let wrapper;
const getGlEmptyStateAttribute = attribute => wrapper.find(GlEmptyState).attributes(attribute);
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(JiraImportSetup, { wrapper = shallowMount(JiraImportSetup, {
propsData: { propsData: {
...@@ -17,12 +20,16 @@ describe('JiraImportSetup', () => { ...@@ -17,12 +20,16 @@ describe('JiraImportSetup', () => {
wrapper = null; wrapper = null;
}); });
it('displays a message to the user', () => { it('contains illustration', () => {
const message = 'You will first need to set up Jira Integration to use this feature.'; expect(getGlEmptyStateAttribute('svgpath')).toBe('illustration.svg');
expect(wrapper.find('p').text()).toBe(message); });
it('contains a description', () => {
const description = 'You will first need to set up Jira Integration to use this feature.';
expect(getGlEmptyStateAttribute('description')).toBe(description);
}); });
it('contains button to set up Jira integration', () => { it('contains button text', () => {
expect(wrapper.find('a').text()).toBe('Set up Jira Integration'); expect(getGlEmptyStateAttribute('primarybuttontext')).toBe('Set up Jira Integration');
}); });
}); });
import { IMPORT_STATE, isInProgress } from '~/jira_import/utils';
describe('isInProgress', () => {
it('returns true when state is IMPORT_STATE.SCHEDULED', () => {
expect(isInProgress(IMPORT_STATE.SCHEDULED)).toBe(true);
});
it('returns true when state is IMPORT_STATE.STARTED', () => {
expect(isInProgress(IMPORT_STATE.STARTED)).toBe(true);
});
it('returns false when state is IMPORT_STATE.FAILED', () => {
expect(isInProgress(IMPORT_STATE.FAILED)).toBe(false);
});
it('returns false when state is IMPORT_STATE.FINISHED', () => {
expect(isInProgress(IMPORT_STATE.FINISHED)).toBe(false);
});
it('returns false when state is IMPORT_STATE.NONE', () => {
expect(isInProgress(IMPORT_STATE.NONE)).toBe(false);
});
it('returns false when state is undefined', () => {
expect(isInProgress()).toBe(false);
});
});
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