Commit 793b0ffd authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '27260-migrate-spec-javascripts-issue_show-to-jest' into 'master'

Migrate issue_show to Jest

Closes #27260

See merge request gitlab-org/gitlab!30720
parents 3e35595f 065cef41
......@@ -329,7 +329,7 @@ export default {
},
deleteIssuable(payload) {
this.service
return this.service
.deleteIssuable(payload)
.then(res => res.data)
.then(data => {
......@@ -340,7 +340,7 @@ export default {
})
.catch(() => {
createFlash(
sprintf(s__('Error deleting %{issuableType}'), { issuableType: this.issuableType }),
sprintf(s__('Error deleting %{issuableType}'), { issuableType: this.issuableType }),
);
});
},
......@@ -365,7 +365,12 @@ export default {
:issuable-type="issuableType"
/>
<recaptcha-modal v-show="showRecaptcha" :html="recaptchaHTML" @close="closeRecaptchaModal" />
<recaptcha-modal
v-show="showRecaptcha"
ref="recaptchaModal"
:html="recaptchaHTML"
@close="closeRecaptchaModal"
/>
</div>
<div v-else>
<title-component
......
......@@ -8395,7 +8395,7 @@ msgstr ""
msgid "Error creating label."
msgstr ""
msgid "Error deleting %{issuableType}"
msgid "Error deleting %{issuableType}"
msgstr ""
msgid "Error deleting project. Check logs for error details."
......
/* eslint-disable no-unused-vars */
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import GLDropdown from '~/gl_dropdown';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import '~/behaviors/markdown/render_gfm';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
......@@ -13,6 +12,9 @@ function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
}
jest.mock('~/lib/utils/url_utility');
jest.mock('~/issue_show/event_hub');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
describe('Issuable output', () => {
......@@ -20,9 +22,10 @@ describe('Issuable output', () => {
let realtimeRequestCount = 0;
let vm;
beforeEach(done => {
beforeEach(() => {
setFixtures(`
<div>
<title>Title</title>
<div class="detail-page-description content-block">
<details open>
<summary>One</summary>
......@@ -35,7 +38,6 @@ describe('Issuable output', () => {
<span id="task_status"></span>
</div>
`);
spyOn(eventHub, '$emit');
const IssuableDescriptionComponent = Vue.extend(issuableApp);
......@@ -53,7 +55,7 @@ describe('Issuable output', () => {
canUpdate: true,
canDestroy: true,
endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes',
updateEndpoint: gl.TEST_HOST,
updateEndpoint: TEST_HOST,
issuableRef: '#1',
initialTitleHtml: '',
initialTitleText: '',
......@@ -67,8 +69,6 @@ describe('Issuable output', () => {
issuableTemplateNamesPath: '/issuable-templates-path',
},
}).$mount();
setTimeout(done);
});
afterEach(() => {
......@@ -79,9 +79,10 @@ describe('Issuable output', () => {
vm.$destroy();
});
it('should render a title/description/edited and update title/description/edited on update', done => {
it('should render a title/description/edited and update title/description/edited on update', () => {
let editedText;
Vue.nextTick()
return axios
.waitForAll()
.then(() => {
editedText = vm.$el.querySelector('.edited-text');
})
......@@ -100,8 +101,8 @@ describe('Issuable output', () => {
})
.then(() => {
vm.poll.makeRequest();
return axios.waitForAll();
})
.then(() => new Promise(resolve => setTimeout(resolve)))
.then(() => {
expect(document.querySelector('title').innerText).toContain('2 (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
......@@ -115,312 +116,239 @@ describe('Issuable output', () => {
expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
expect(editedText.querySelector('time')).toBeTruthy();
expect(vm.state.lock_version).toEqual(2);
})
.then(done)
.catch(done.fail);
});
});
it('shows actions if permissions are correct', done => {
it('shows actions if permissions are correct', () => {
vm.showForm = true;
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.btn')).not.toBeNull();
done();
});
});
it('does not show actions if permissions are incorrect', done => {
it('does not show actions if permissions are incorrect', () => {
vm.showForm = true;
vm.canUpdate = false;
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.btn')).toBeNull();
done();
});
});
it('does not update formState if form is already open', done => {
it('does not update formState if form is already open', () => {
vm.updateAndShowForm();
vm.state.titleText = 'testing 123';
vm.updateAndShowForm();
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(vm.store.formState.title).not.toBe('testing 123');
});
});
it('opens reCAPTCHA modal if update rejected as spam', () => {
let modal;
done();
jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
data: {
recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
},
});
vm.canUpdate = true;
vm.showForm = true;
return vm
.$nextTick()
.then(() => {
vm.$refs.recaptchaModal.scriptSrc = '//scriptsrc';
return vm.updateIssuable();
})
.then(() => {
modal = vm.$el.querySelector('.js-recaptcha-modal');
expect(modal.style.display).not.toEqual('none');
expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
})
.then(() => {
modal.querySelector('.close').click();
return vm.$nextTick();
})
.then(() => {
expect(modal.style.display).toEqual('none');
expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
});
});
describe('updateIssuable', () => {
it('fetches new data after update', done => {
spyOn(vm, 'updateStoreState').and.callThrough();
spyOn(vm.service, 'getData').and.callThrough();
spyOn(vm.service, 'updateIssuable').and.returnValue(
Promise.resolve({
data: { web_url: window.location.pathname },
}),
);
vm.updateIssuable()
.then(() => {
expect(vm.updateStoreState).toHaveBeenCalled();
expect(vm.service.getData).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
it('fetches new data after update', () => {
const updateStoreSpy = jest.spyOn(vm, 'updateStoreState');
const getDataSpy = jest.spyOn(vm.service, 'getData');
jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
data: { web_url: window.location.pathname },
});
return vm.updateIssuable().then(() => {
expect(updateStoreSpy).toHaveBeenCalled();
expect(getDataSpy).toHaveBeenCalled();
});
});
it('correctly updates issuable data', done => {
spyOn(vm.service, 'updateIssuable').and.returnValue(
Promise.resolve({
data: { web_url: window.location.pathname },
}),
);
it('correctly updates issuable data', () => {
const spy = jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
data: { web_url: window.location.pathname },
});
vm.updateIssuable()
.then(() => {
expect(vm.service.updateIssuable).toHaveBeenCalledWith(vm.formState);
expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
})
.then(done)
.catch(done.fail);
return vm.updateIssuable().then(() => {
expect(spy).toHaveBeenCalledWith(vm.formState);
expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
});
});
it('does not redirect if issue has not moved', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.returnValue(
Promise.resolve({
data: {
web_url: window.location.pathname,
confidential: vm.isConfidential,
},
}),
);
vm.updateIssuable();
it('does not redirect if issue has not moved', () => {
jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
data: {
web_url: window.location.pathname,
confidential: vm.isConfidential,
},
});
setTimeout(() => {
return vm.updateIssuable().then(() => {
expect(visitUrl).not.toHaveBeenCalled();
done();
});
});
it('redirects if returned web_url has changed', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.returnValue(
Promise.resolve({
data: {
web_url: '/testing-issue-move',
confidential: vm.isConfidential,
},
}),
);
it('redirects if returned web_url has changed', () => {
jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
data: {
web_url: '/testing-issue-move',
confidential: vm.isConfidential,
},
});
vm.updateIssuable();
setTimeout(() => {
return vm.updateIssuable().then(() => {
expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move');
done();
});
});
describe('shows dialog when issue has unsaved changed', () => {
it('confirms on title change', done => {
it('confirms on title change', () => {
vm.showForm = true;
vm.state.titleText = 'title has changed';
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(e.returnValue).not.toBeNull();
done();
});
});
it('confirms on description change', done => {
it('confirms on description change', () => {
vm.showForm = true;
vm.state.descriptionText = 'description has changed';
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(e.returnValue).not.toBeNull();
done();
});
});
it('does nothing when nothing has changed', done => {
it('does nothing when nothing has changed', () => {
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(e.returnValue).toBeNull();
done();
});
});
});
describe('error when updating', () => {
it('closes form on error', done => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject());
vm.updateIssuable();
setTimeout(() => {
it('closes form on error', () => {
jest.spyOn(vm.service, 'updateIssuable').mockRejectedValue();
return vm.updateIssuable().then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating issue`,
);
done();
});
});
it('returns the correct error message for issuableType', done => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject());
it('returns the correct error message for issuableType', () => {
jest.spyOn(vm.service, 'updateIssuable').mockRejectedValue();
vm.issuableType = 'merge request';
Vue.nextTick(() => {
vm.updateIssuable();
setTimeout(() => {
return vm
.$nextTick()
.then(vm.updateIssuable)
.then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating merge request`,
);
done();
});
});
});
it('shows error message from backend if exists', done => {
it('shows error message from backend if exists', () => {
const msg = 'Custom error message from backend';
spyOn(vm.service, 'updateIssuable').and.callFake(
// eslint-disable-next-line prefer-promise-reject-errors
() => Promise.reject({ response: { data: { errors: [msg] } } }),
);
jest
.spyOn(vm.service, 'updateIssuable')
.mockRejectedValue({ response: { data: { errors: [msg] } } });
vm.updateIssuable();
setTimeout(() => {
return vm.updateIssuable().then(() => {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`${vm.defaultErrorMessage}. ${msg}`,
);
done();
});
});
});
});
it('opens reCAPTCHA modal if update rejected as spam', done => {
function mockScriptSrc() {
const recaptchaChild = vm.$children.find(
// eslint-disable-next-line no-underscore-dangle
child => child.$options._componentTag === 'recaptcha-modal',
);
recaptchaChild.scriptSrc = '//scriptsrc';
}
let modal;
const promise = new Promise(resolve => {
resolve({
describe('deleteIssuable', () => {
it('changes URL when deleted', () => {
jest.spyOn(vm.service, 'deleteIssuable').mockResolvedValue({
data: {
recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
web_url: '/test',
},
});
});
spyOn(vm.service, 'updateIssuable').and.returnValue(promise);
vm.canUpdate = true;
vm.showForm = true;
vm.$nextTick()
.then(() => mockScriptSrc())
.then(() => vm.updateIssuable())
.then(promise)
.then(() => setTimeoutPromise())
.then(() => {
modal = vm.$el.querySelector('.js-recaptcha-modal');
expect(modal.style.display).not.toEqual('none');
expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
})
.then(() => modal.querySelector('.close').click())
.then(() => vm.$nextTick())
.then(() => {
expect(modal.style.display).toEqual('none');
expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
})
.then(done)
.catch(done.fail);
});
describe('deleteIssuable', () => {
it('changes URL when deleted', done => {
const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'deleteIssuable').and.returnValue(
Promise.resolve({
data: {
web_url: '/test',
},
}),
);
vm.deleteIssuable();
setTimeout(() => {
return vm.deleteIssuable().then(() => {
expect(visitUrl).toHaveBeenCalledWith('/test');
done();
});
});
it('stops polling when deleting', done => {
spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.poll, 'stop').and.callThrough();
spyOn(vm.service, 'deleteIssuable').and.returnValue(
Promise.resolve({
data: {
web_url: '/test',
},
}),
);
vm.deleteIssuable();
setTimeout(() => {
expect(vm.poll.stop).toHaveBeenCalledWith();
it('stops polling when deleting', () => {
const spy = jest.spyOn(vm.poll, 'stop');
jest.spyOn(vm.service, 'deleteIssuable').mockResolvedValue({
data: {
web_url: '/test',
},
});
done();
return vm.deleteIssuable().then(() => {
expect(spy).toHaveBeenCalledWith();
});
});
it('closes form on error', done => {
spyOn(vm.service, 'deleteIssuable').and.returnValue(Promise.reject());
it('closes form on error', () => {
jest.spyOn(vm.service, 'deleteIssuable').mockRejectedValue();
vm.deleteIssuable();
setTimeout(() => {
return vm.deleteIssuable().then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
'Error deleting issue',
);
done();
});
});
});
describe('updateAndShowForm', () => {
it('shows locked warning if form is open & data is different', done => {
vm.$nextTick()
it('shows locked warning if form is open & data is different', () => {
return vm
.$nextTick()
.then(() => {
vm.updateAndShowForm();
......@@ -436,44 +364,38 @@ describe('Issuable output', () => {
expect(vm.formState.lockedWarningVisible).toEqual(true);
expect(vm.formState.lock_version).toEqual(1);
expect(vm.$el.querySelector('.alert')).not.toBeNull();
})
.then(done)
.catch(done.fail);
});
});
});
describe('requestTemplatesAndShowForm', () => {
let formSpy;
beforeEach(() => {
spyOn(vm, 'updateAndShowForm');
formSpy = jest.spyOn(vm, 'updateAndShowForm');
});
it('shows the form if template names request is successful', done => {
it('shows the form if template names request is successful', () => {
const mockData = [{ name: 'Bug' }];
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
vm.requestTemplatesAndShowForm()
.then(() => {
expect(vm.updateAndShowForm).toHaveBeenCalledWith(mockData);
})
.then(done)
.catch(done.fail);
return vm.requestTemplatesAndShowForm().then(() => {
expect(formSpy).toHaveBeenCalledWith(mockData);
});
});
it('shows the form if template names request failed', done => {
it('shows the form if template names request failed', () => {
mock
.onGet('/issuable-templates-path')
.reply(() => Promise.reject(new Error('something went wrong')));
vm.requestTemplatesAndShowForm()
.then(() => {
expect(document.querySelector('.flash-container .flash-text').textContent).toContain(
'Error updating issue',
);
return vm.requestTemplatesAndShowForm().then(() => {
expect(document.querySelector('.flash-container .flash-text').textContent).toContain(
'Error updating issue',
);
expect(vm.updateAndShowForm).toHaveBeenCalledWith();
})
.then(done)
.catch(done.fail);
expect(formSpy).toHaveBeenCalledWith();
});
});
});
......@@ -490,32 +412,26 @@ describe('Issuable output', () => {
});
describe('updateStoreState', () => {
it('should make a request and update the state of the store', done => {
it('should make a request and update the state of the store', () => {
const data = { foo: 1 };
spyOn(vm.store, 'updateState');
spyOn(vm.service, 'getData').and.returnValue(Promise.resolve({ data }));
const getDataSpy = jest.spyOn(vm.service, 'getData').mockResolvedValue({ data });
const updateStateSpy = jest.spyOn(vm.store, 'updateState').mockImplementation(jest.fn);
vm.updateStoreState()
.then(() => {
expect(vm.service.getData).toHaveBeenCalled();
expect(vm.store.updateState).toHaveBeenCalledWith(data);
})
.then(done)
.catch(done.fail);
return vm.updateStoreState().then(() => {
expect(getDataSpy).toHaveBeenCalled();
expect(updateStateSpy).toHaveBeenCalledWith(data);
});
});
it('should show error message if store update fails', done => {
spyOn(vm.service, 'getData').and.returnValue(Promise.reject());
it('should show error message if store update fails', () => {
jest.spyOn(vm.service, 'getData').mockRejectedValue();
vm.issuableType = 'merge request';
vm.updateStoreState()
.then(() => {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating ${vm.issuableType}`,
);
})
.then(done)
.catch(done.fail);
return vm.updateStoreState().then(() => {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating ${vm.issuableType}`,
);
});
});
});
......
import $ from 'jquery';
import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mountComponent from 'helpers/vue_mount_component_helper';
import { TEST_HOST } from 'helpers/test_constants';
import Description from '~/issue_show/components/description.vue';
import TaskList from '~/task_list';
jest.mock('~/task_list');
describe('Description component', () => {
let vm;
......@@ -13,7 +17,7 @@ describe('Description component', () => {
descriptionText: 'test',
updatedAt: new Date().toString(),
taskStatus: '',
updateUrl: gl.TEST_HOST,
updateUrl: TEST_HOST,
};
beforeEach(() => {
......@@ -39,25 +43,26 @@ describe('Description component', () => {
$('.issuable-meta .flash-container').remove();
});
it('animates description changes', done => {
it('animates description changes', () => {
vm.descriptionHtml = 'changed';
Vue.nextTick(() => {
expect(
vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'),
).toBeTruthy();
setTimeout(() => {
return vm
.$nextTick()
.then(() => {
expect(
vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'),
).toBeTruthy();
jest.runAllTimers();
return vm.$nextTick();
})
.then(() => {
expect(
vm.$el.querySelector('.md').classList.contains('issue-realtime-trigger-pulse'),
).toBeTruthy();
done();
});
});
});
it('opens reCAPTCHA dialog if update rejected as spam', done => {
it('opens reCAPTCHA dialog if update rejected as spam', () => {
let modal;
const recaptchaChild = vm.$children.find(
// eslint-disable-next-line no-underscore-dangle
......@@ -70,7 +75,8 @@ describe('Description component', () => {
recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
});
vm.$nextTick()
return vm
.$nextTick()
.then(() => {
modal = vm.$el.querySelector('.js-recaptcha-modal');
......@@ -83,128 +89,105 @@ describe('Description component', () => {
.then(() => {
expect(modal.style.display).toEqual('none');
expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
})
.then(done)
.catch(done.fail);
});
});
describe('TaskList', () => {
let TaskList;
it('applies syntax highlighting and math when description changed', () => {
const vmSpy = jest.spyOn(vm, 'renderGFM');
const prototypeSpy = jest.spyOn($.prototype, 'renderGFM');
vm.descriptionHtml = 'changed';
return vm.$nextTick().then(() => {
expect(vm.$refs['gfm-content']).toBeDefined();
expect(vmSpy).toHaveBeenCalled();
expect(prototypeSpy).toHaveBeenCalled();
expect($.prototype.renderGFM).toHaveBeenCalled();
});
});
it('sets data-update-url', () => {
expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(TEST_HOST);
});
describe('TaskList', () => {
beforeEach(() => {
vm.$destroy();
TaskList.mockClear();
vm = mountComponent(
DescriptionComponent,
Object.assign({}, props, {
issuableType: 'issuableType',
}),
);
TaskList = spyOnDependency(Description, 'TaskList');
});
it('re-inits the TaskList when description changed', done => {
it('re-inits the TaskList when description changed', () => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
expect(TaskList).toHaveBeenCalled();
done();
});
expect(TaskList).toHaveBeenCalled();
});
it('does not re-init the TaskList when canUpdate is false', done => {
it('does not re-init the TaskList when canUpdate is false', () => {
vm.canUpdate = false;
vm.descriptionHtml = 'changed';
setTimeout(() => {
expect(TaskList).not.toHaveBeenCalled();
done();
});
expect(TaskList).toHaveBeenCalledTimes(1);
});
it('calls with issuableType dataType', done => {
it('calls with issuableType dataType', () => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
expect(TaskList).toHaveBeenCalledWith({
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
onSuccess: jasmine.any(Function),
onError: jasmine.any(Function),
lockVersion: 0,
});
done();
expect(TaskList).toHaveBeenCalledWith({
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
onSuccess: expect.any(Function),
onError: expect.any(Function),
lockVersion: 0,
});
});
});
describe('taskStatus', () => {
it('adds full taskStatus', done => {
it('adds full taskStatus', () => {
vm.taskStatus = '1 of 1';
setTimeout(() => {
return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(
'1 of 1',
);
done();
});
});
it('adds short taskStatus', done => {
it('adds short taskStatus', () => {
vm.taskStatus = '1 of 1';
setTimeout(() => {
return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe(
'1/1 task',
);
done();
});
});
it('clears task status text when no tasks are present', done => {
it('clears task status text when no tasks are present', () => {
vm.taskStatus = '0 of 0';
setTimeout(() => {
return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe('');
done();
});
});
});
it('applies syntax highlighting and math when description changed', done => {
spyOn(vm, 'renderGFM').and.callThrough();
spyOn($.prototype, 'renderGFM').and.callThrough();
vm.descriptionHtml = 'changed';
Vue.nextTick(() => {
setTimeout(() => {
expect(vm.$refs['gfm-content']).toBeDefined();
expect(vm.renderGFM).toHaveBeenCalled();
expect($.prototype.renderGFM).toHaveBeenCalled();
done();
});
});
});
it('sets data-update-url', () => {
expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST);
});
describe('taskListUpdateError', () => {
it('should create flash notification and emit an event to parent', () => {
const msg =
'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.';
spyOn(vm, '$emit');
const spy = jest.spyOn(vm, '$emit');
vm.taskListUpdateError();
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
expect(vm.$emit).toHaveBeenCalledWith('taskListUpdateFailed');
expect(spy).toHaveBeenCalledWith('taskListUpdateFailed');
});
});
});
......@@ -5,7 +5,7 @@ describe('Issue description template component', () => {
let vm;
let formState;
beforeEach(done => {
beforeEach(() => {
const Component = Vue.extend(descriptionTemplate);
formState = {
description: 'test',
......@@ -19,8 +19,6 @@ describe('Issue description template component', () => {
projectNamespace: '/',
},
}).$mount();
Vue.nextTick(done);
});
it('renders templates as JSON array in data attribute', () => {
......
import Vue from 'vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mountComponent from 'helpers/vue_mount_component_helper';
import formComponent from '~/issue_show/components/form.vue';
import Autosave from '~/autosave';
import eventHub from '~/issue_show/event_hub';
jest.mock('~/autosave');
describe('Inline edit form component', () => {
let vm;
const defaultProps = {
......@@ -65,18 +68,16 @@ describe('Inline edit form component', () => {
});
describe('autosave', () => {
let autosaveObj;
let autosave;
let spy;
beforeEach(() => {
autosaveObj = { reset: jasmine.createSpy() };
autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
spy = jest.spyOn(Autosave.prototype, 'reset');
});
it('initialized Autosave on mount', () => {
createComponent();
expect(autosave).toHaveBeenCalledTimes(2);
expect(Autosave).toHaveBeenCalledTimes(2);
});
it('calls reset on autosave when eventHub emits appropriate events', () => {
......@@ -84,15 +85,15 @@ describe('Inline edit form component', () => {
eventHub.$emit('close.form');
expect(autosaveObj.reset).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledTimes(2);
eventHub.$emit('delete.issuable');
expect(autosaveObj.reset).toHaveBeenCalledTimes(4);
expect(spy).toHaveBeenCalledTimes(4);
eventHub.$emit('update.issuable');
expect(autosaveObj.reset).toHaveBeenCalledTimes(6);
expect(spy).toHaveBeenCalledTimes(6);
});
});
});
......@@ -5,8 +5,9 @@ import eventHub from '~/issue_show/event_hub';
describe('Title component', () => {
let vm;
beforeEach(() => {
setFixtures(`<title />`);
const Component = Vue.extend(titleComponent);
const store = new Store({
titleHtml: '',
......@@ -28,51 +29,39 @@ describe('Title component', () => {
expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>');
});
it('updates page title when changing titleHtml', done => {
spyOn(vm, 'setPageTitle');
it('updates page title when changing titleHtml', () => {
const spy = jest.spyOn(vm, 'setPageTitle');
vm.titleHtml = 'test';
Vue.nextTick(() => {
expect(vm.setPageTitle).toHaveBeenCalled();
done();
return vm.$nextTick().then(() => {
expect(spy).toHaveBeenCalled();
});
});
it('animates title changes', done => {
it('animates title changes', () => {
vm.titleHtml = 'test';
Vue.nextTick(() => {
expect(
vm.$el.querySelector('.title').classList.contains('issue-realtime-pre-pulse'),
).toBeTruthy();
setTimeout(() => {
expect(
vm.$el.querySelector('.title').classList.contains('issue-realtime-trigger-pulse'),
).toBeTruthy();
done();
return vm
.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-pre-pulse');
jest.runAllTimers();
return vm.$nextTick();
})
.then(() => {
expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-trigger-pulse');
});
});
});
it('updates page title after changing title', done => {
it('updates page title after changing title', () => {
vm.titleHtml = 'changed';
vm.titleText = 'changed';
Vue.nextTick(() => {
return vm.$nextTick().then(() => {
expect(document.querySelector('title').textContent.trim()).toContain('changed');
done();
});
});
describe('inline edit button', () => {
beforeEach(() => {
spyOn(eventHub, '$emit');
});
it('should not show by default', () => {
expect(vm.$el.querySelector('.btn-edit')).toBeNull();
});
......@@ -92,6 +81,7 @@ describe('Title component', () => {
});
it('should trigger open.form event when clicked', () => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm.showInlineEditButton = true;
vm.canUpdate = true;
......
export * from '../../frontend/issue_show/helpers.js';
export * from '../../frontend/issue_show/mock_data';
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