Update vue-resource

This commit is contained in:
Filipa Lacerda 2017-07-12 14:47:09 +00:00 committed by Phil Hughes
parent fd692d1066
commit aaa78199c2
28 changed files with 328 additions and 171 deletions

View file

@ -51,8 +51,9 @@ export default () => {
methods: { methods: {
loadFile() { loadFile() {
this.$http.get(el.dataset.endpoint) this.$http.get(el.dataset.endpoint)
.then(response => response.json())
.then((res) => { .then((res) => {
this.json = res.json(); this.json = res;
this.loading = false; this.loading = false;
}) })
.catch((e) => { .catch((e) => {

View file

@ -81,8 +81,9 @@ $(() => {
mounted () { mounted () {
Store.disabled = this.disabled; Store.disabled = this.disabled;
gl.boardService.all() gl.boardService.all()
.then(response => response.json())
.then((resp) => { .then((resp) => {
resp.json().forEach((board) => { resp.forEach((board) => {
const list = Store.addList(board, this.defaultAvatar); const list = Store.addList(board, this.defaultAvatar);
if (list.type === 'closed') { if (list.type === 'closed') {
@ -97,7 +98,8 @@ $(() => {
Store.addBlankState(); Store.addBlankState();
this.loading = false; this.loading = false;
}).catch(() => new Flash('An error occurred. Please try again.')); })
.catch(() => new Flash('An error occurred. Please try again.'));
}, },
methods: { methods: {
updateTokens() { updateTokens() {

View file

@ -64,8 +64,9 @@ export default {
// Save the labels // Save the labels
gl.boardService.generateDefaultLists() gl.boardService.generateDefaultLists()
.then((resp) => { .then(resp => resp.json())
resp.json().forEach((listObj) => { .then((data) => {
data.forEach((listObj) => {
const list = Store.findList('title', listObj.title); const list = Store.findList('title', listObj.title);
list.id = listObj.id; list.id = listObj.id;

View file

@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
return gl.boardService.getBacklog(queryData(this.filter.path, { return gl.boardService.getBacklog(queryData(this.filter.path, {
page: this.page, page: this.page,
per: this.perPage, per: this.perPage,
})).then((res) => { }))
const data = res.json(); .then(resp => resp.json())
.then((data) => {
if (clearIssues) { if (clearIssues) {
this.issues = []; this.issues = [];
} }

View file

@ -40,9 +40,8 @@ class List {
save () { save () {
return gl.boardService.createList(this.label.id) return gl.boardService.createList(this.label.id)
.then((resp) => { .then(resp => resp.json())
const data = resp.json(); .then((data) => {
this.id = data.id; this.id = data.id;
this.type = data.list_type; this.type = data.list_type;
this.position = data.position; this.position = data.position;
@ -91,8 +90,8 @@ class List {
} }
return gl.boardService.getIssuesForList(this.id, data) return gl.boardService.getIssuesForList(this.id, data)
.then((resp) => { .then(resp => resp.json())
const data = resp.json(); .then((data) => {
this.loading = false; this.loading = false;
this.issuesSize = data.size; this.issuesSize = data.size;
@ -109,8 +108,8 @@ class List {
this.issuesSize += 1; this.issuesSize += 1;
return gl.boardService.newIssue(this.id, issue) return gl.boardService.newIssue(this.id, issue)
.then((resp) => { .then(resp => resp.json())
const data = resp.json(); .then((data) => {
issue.id = data.iid; issue.id = data.iid;
if (this.issuesSize > 1) { if (this.issuesSize > 1) {

View file

@ -23,11 +23,6 @@ class BoardService {
url: bulkUpdatePath, url: bulkUpdatePath,
}, },
}); });
Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
next();
});
} }
all () { all () {

View file

@ -51,11 +51,11 @@
}, },
methods: { methods: {
successCallback(resp) { successCallback(resp) {
const response = resp.json(); return resp.json().then((response) => {
// depending of the endpoint the response can either bring a `pipelines` key or not. // depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response; const pipelines = response.pipelines || response;
this.setCommonData(pipelines); this.setCommonData(pipelines);
});
}, },
}, },
}; };

View file

@ -1,4 +1,4 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */ /* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, max-len */
/* global CommentsStore */ /* global CommentsStore */
/* global ResolveService */ /* global ResolveService */
/* global Flash */ /* global Flash */
@ -64,8 +64,6 @@ const ResolveBtn = Vue.extend({
}); });
}, },
resolve: function () { resolve: function () {
const errorFlashMsg = 'An error occurred when trying to resolve a comment. Please try again.';
if (!this.canResolve) return; if (!this.canResolve) return;
let promise; let promise;
@ -79,24 +77,20 @@ const ResolveBtn = Vue.extend({
.resolve(this.noteId); .resolve(this.noteId);
} }
promise.then((response) => { promise
.then(resp => resp.json())
.then((data) => {
this.loading = false; this.loading = false;
if (response.status === 200) {
const data = response.json();
const resolved_by = data ? data.resolved_by : null; const resolved_by = data ? data.resolved_by : null;
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by); CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
this.discussion.updateHeadline(data); this.discussion.updateHeadline(data);
gl.mrWidget.checkStatus(); gl.mrWidget.checkStatus();
} else {
new Flash(errorFlashMsg);
}
this.updateTooltip(); this.updateTooltip();
}).catch(() => { })
new Flash(errorFlashMsg); .catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.'));
});
} }
}, },
mounted: function () { mounted: function () {

View file

@ -1,4 +1,3 @@
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
/* global Flash */ /* global Flash */
/* global CommentsStore */ /* global CommentsStore */
@ -32,27 +31,22 @@ class ResolveServiceClass {
promise = this.resolveAll(mergeRequestId, discussionId); promise = this.resolveAll(mergeRequestId, discussionId);
} }
promise.then((response) => { promise
.then(resp => resp.json())
.then((data) => {
discussion.loading = false; discussion.loading = false;
const resolvedBy = data ? data.resolved_by : null;
if (response.status === 200) {
const data = response.json();
const resolved_by = data ? data.resolved_by : null;
if (isResolved) { if (isResolved) {
discussion.unResolveAllNotes(); discussion.unResolveAllNotes();
} else { } else {
discussion.resolveAllNotes(resolved_by); discussion.resolveAllNotes(resolvedBy);
} }
gl.mrWidget.checkStatus(); gl.mrWidget.checkStatus();
discussion.updateHeadline(data); discussion.updateHeadline(data);
} else { })
throw new Error('An error occurred when trying to resolve discussion.'); .catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.'));
}
}).catch(() => {
new Flash('An error occurred when trying to resolve a discussion. Please try again.');
});
} }
resolveAll(mergeRequestId, discussionId) { resolveAll(mergeRequestId, discussionId) {
@ -62,7 +56,7 @@ class ResolveServiceClass {
return this.discussionResource.save({ return this.discussionResource.save({
mergeRequestId, mergeRequestId,
discussionId discussionId,
}, {}); }, {});
} }
@ -73,7 +67,7 @@ class ResolveServiceClass {
return this.discussionResource.delete({ return this.discussionResource.delete({
mergeRequestId, mergeRequestId,
discussionId discussionId,
}, {}); }, {});
} }
} }

View file

@ -1,17 +1,15 @@
export default { export default {
methods: { methods: {
saveData(resp) { saveData(resp) {
const response = { const headers = resp.headers;
headers: resp.headers, return resp.json().then((response) => {
body: resp.json(),
};
this.isLoading = false; this.isLoading = false;
this.store.storeAvailableCount(response.body.available_count); this.store.storeAvailableCount(response.available_count);
this.store.storeStoppedCount(response.body.stopped_count); this.store.storeStoppedCount(response.stopped_count);
this.store.storeEnvironments(response.body.environments); this.store.storeEnvironments(response.environments);
this.store.setPagination(response.headers); this.store.setPagination(headers);
});
}, },
}, },
}; };

View file

@ -99,8 +99,10 @@ document.addEventListener('DOMContentLoaded', () => {
page: currentPath, page: currentPath,
}, document.title, currentPath); }, document.title, currentPath);
this.updateGroups(response.json()); return response.json().then((data) => {
this.updateGroups(data);
this.updatePagination(response.headers); this.updatePagination(response.headers);
});
}) })
.catch(this.handleErrorResponse); .catch(this.handleErrorResponse);
}, },
@ -114,18 +116,19 @@ document.addEventListener('DOMContentLoaded', () => {
}, },
leaveGroup(group, collection) { leaveGroup(group, collection) {
this.service.leaveGroup(group.leavePath) this.service.leaveGroup(group.leavePath)
.then(resp => resp.json())
.then((response) => { .then((response) => {
$.scrollTo(0); $.scrollTo(0);
this.store.removeGroup(group, collection); this.store.removeGroup(group, collection);
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Flash(response.json().notice, 'notice'); new Flash(response.notice, 'notice');
}) })
.catch((response) => { .catch((error) => {
let message = 'An error occurred. Please try again.'; let message = 'An error occurred. Please try again.';
if (response.status === 403) { if (error.status === 403) {
message = 'Failed to leave the group. Please make sure you are not the only owner'; message = 'Failed to leave the group. Please make sure you are not the only owner';
} }

View file

@ -202,10 +202,7 @@ export default {
this.poll = new Poll({ this.poll = new Poll({
resource: this.service, resource: this.service,
method: 'getData', method: 'getData',
successCallback: (res) => { successCallback: res => res.json().then(data => this.store.updateState(data)),
const data = res.json();
this.store.updateState(data);
},
errorCallback(err) { errorCallback(err) {
throw new Error(err); throw new Error(err);
}, },

View file

@ -54,9 +54,8 @@ export default class JobMediator {
} }
successCallback(response) { successCallback(response) {
const data = response.json();
this.state.isLoading = false; this.state.isLoading = false;
this.store.storeJob(data); return response.json().then(data => this.store.storeJob(data));
} }
errorCallback() { errorCallback() {

View file

@ -1270,7 +1270,7 @@ export default class Notes {
<div class="timeline-entry-inner"> <div class="timeline-entry-inner">
<div class="timeline-icon"> <div class="timeline-icon">
<a href="/${currentUsername}"> <a href="/${currentUsername}">
<img class="avatar s40" src="${currentUserAvatar}"> <img class="avatar s40" src="${currentUserAvatar}" />
</a> </a>
</div> </div>
<div class="timeline-content ${discussionClass}"> <div class="timeline-content ${discussionClass}">

View file

@ -129,14 +129,11 @@
}, },
successCallback(resp) { successCallback(resp) {
const response = { return resp.json().then((response) => {
headers: resp.headers, this.store.storeCount(response.count);
body: resp.json(), this.store.storePagination(resp.headers);
}; this.setCommonData(response.pipelines);
});
this.store.storeCount(response.body.count);
this.store.storePagination(response.headers);
this.setCommonData(response.body.pipelines);
}, },
}, },
}; };

View file

@ -73,8 +73,9 @@ export default {
fetchJobs() { fetchJobs() {
this.$http.get(this.stage.dropdown_path) this.$http.get(this.stage.dropdown_path)
.then((response) => { .then(response => response.json())
this.dropdownContent = response.json().html; .then((data) => {
this.dropdownContent = data.html;
this.isLoading = false; this.isLoading = false;
}) })
.catch(() => { .catch(() => {

View file

@ -40,10 +40,10 @@ export default class pipelinesMediator {
} }
successCallback(response) { successCallback(response) {
const data = response.json(); return response.json().then((data) => {
this.state.isLoading = false; this.state.isLoading = false;
this.store.storePipeline(data); this.store.storePipeline(data);
});
} }
errorCallback() { errorCallback() {

View file

@ -28,8 +28,8 @@ export default class SidebarMediator {
fetch() { fetch() {
this.service.get() this.service.get()
.then((response) => { .then(response => response.json())
const data = response.json(); .then((data) => {
this.store.setAssigneeData(data); this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data); this.store.setTimeTrackingData(data);
}) })

View file

@ -44,9 +44,8 @@
text: this.$slots.textarea[0].elm.value, text: this.$slots.textarea[0].elm.value,
}, },
) )
.then((res) => { .then(resp => resp.json())
const data = res.json(); .then((data) => {
this.markdownPreviewLoading = false; this.markdownPreviewLoading = false;
this.markdownPreview = data.body; this.markdownPreview = data.body;

View file

@ -14,11 +14,22 @@ Vue.http.interceptors.push((request, next) => {
}); });
}); });
// Inject CSRF token so we don't break any tests. // Inject CSRF token and parse headers.
// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
// and polling.
Vue.http.interceptors.push((request, next) => { Vue.http.interceptors.push((request, next) => {
if ($.rails) { if ($.rails) {
// eslint-disable-next-line no-param-reassign request.headers.set('X-CSRF-Token', $.rails.csrfToken());
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
} }
next();
next((response) => {
// Headers object has a `forEach` property that iterates through all values.
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
// eslint-disable-next-line no-param-reassign
response.headers = headers;
});
}); });

View file

@ -0,0 +1,4 @@
---
title: Updates vue resource and code according to breaking changes
merge_request:
author:

View file

@ -112,7 +112,50 @@ Vue Resource should only be imported in the service file.
Vue.use(VueResource); Vue.use(VueResource);
``` ```
### CSRF token #### Vue-resource gotchas
#### Headers
Headers are being parsed into a plain object in an interceptor.
In Vue-resource 1.x `headers` object was changed into an `Headers` object. In order to not change all old code, an interceptor was added.
If you need to write a unit test that takes the headers in consideration, you need to include an interceptor to parse the headers after your test interceptor.
You can see an example in `spec/javascripts/environments/environment_spec.js`:
```javascript
import { headersInterceptor } from './helpers/vue_resource_helper';
beforeEach(() => {
Vue.http.interceptors.push(myInterceptor);
Vue.http.interceptors.push(headersInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, myInterceptor);
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
```
#### `.json()`
When making a request to the server, you will most likely need to access the body of the response.
Use `.json()` to convert. Because `.json()` returns a Promise the follwoing structure should be used:
```javascript
service.get('url')
.then(resp => resp.json())
.then((data) => {
this.store.storeData(data);
})
.catch(() => new Flash('Something went wrong'));
```
When using `Poll` (`app/assets/javascripts/lib/utils/poll.js`), the `successCallback` needs to handle `.json()` as a Promise:
```javascript
successCallback: (response) => {
return response.json().then((data) => {
// handle the response
});
}
```
#### CSRF token
We use a Vue Resource interceptor to manage the CSRF token. We use a Vue Resource interceptor to manage the CSRF token.
`app/assets/javascripts/vue_shared/vue_resource_interceptor.js` holds all our common interceptors. `app/assets/javascripts/vue_shared/vue_resource_interceptor.js` holds all our common interceptors.
Note: You don't need to load `app/assets/javascripts/vue_shared/vue_resource_interceptor.js` Note: You don't need to load `app/assets/javascripts/vue_shared/vue_resource_interceptor.js`
@ -423,7 +466,11 @@ the response we need:
}); });
``` ```
1. Headers interceptor
Refer to [this section](vue.md#headers)
1. Use `$.mount()` to mount the component 1. Use `$.mount()` to mount the component
```javascript ```javascript
// bad // bad
new Component({ new Component({

View file

@ -58,7 +58,7 @@
"visibilityjs": "^1.2.4", "visibilityjs": "^1.2.4",
"vue": "^2.2.6", "vue": "^2.2.6",
"vue-loader": "^11.3.4", "vue-loader": "^11.3.4",
"vue-resource": "^0.9.3", "vue-resource": "^1.3.4",
"vue-template-compiler": "^2.2.6", "vue-template-compiler": "^2.2.6",
"webpack": "^2.6.1", "webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.8.2" "webpack-bundle-analyzer": "^2.8.2"

View file

@ -2,6 +2,7 @@ import Vue from 'vue';
import '~/flash'; import '~/flash';
import environmentsComponent from '~/environments/components/environment.vue'; import environmentsComponent from '~/environments/components/environment.vue';
import { environment, folder } from './mock_data'; import { environment, folder } from './mock_data';
import { headersInterceptor } from '../helpers/vue_resource_helper';
describe('Environment', () => { describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw'); preloadFixtures('static/environments/environments.html.raw');
@ -25,12 +26,14 @@ describe('Environment', () => {
beforeEach(() => { beforeEach(() => {
Vue.http.interceptors.push(environmentsEmptyResponseInterceptor); Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
Vue.http.interceptors.push(headersInterceptor);
}); });
afterEach(() => { afterEach(() => {
Vue.http.interceptors = _.without( Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsEmptyResponseInterceptor, Vue.http.interceptors, environmentsEmptyResponseInterceptor,
); );
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
}); });
it('should render the empty state', (done) => { it('should render the empty state', (done) => {
@ -54,6 +57,10 @@ describe('Environment', () => {
describe('with paginated environments', () => { describe('with paginated environments', () => {
const environmentsResponseInterceptor = (request, next) => { const environmentsResponseInterceptor = (request, next) => {
next((response) => {
response.headers.set('X-nExt-pAge', '2');
});
next(request.respondWith(JSON.stringify({ next(request.respondWith(JSON.stringify({
environments: [environment], environments: [environment],
stopped_count: 1, stopped_count: 1,
@ -73,6 +80,7 @@ describe('Environment', () => {
beforeEach(() => { beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor); Vue.http.interceptors.push(environmentsResponseInterceptor);
Vue.http.interceptors.push(headersInterceptor);
component = new EnvironmentsComponent({ component = new EnvironmentsComponent({
el: document.querySelector('#environments-list-view'), el: document.querySelector('#environments-list-view'),
}); });
@ -82,6 +90,7 @@ describe('Environment', () => {
Vue.http.interceptors = _.without( Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor, Vue.http.interceptors, environmentsResponseInterceptor,
); );
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
}); });
it('should render a table with environments', (done) => { it('should render a table with environments', (done) => {

View file

@ -2,6 +2,7 @@ import Vue from 'vue';
import '~/flash'; import '~/flash';
import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue'; import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import { environmentsList } from '../mock_data'; import { environmentsList } from '../mock_data';
import { headersInterceptor } from '../../helpers/vue_resource_helper';
describe('Environments Folder View', () => { describe('Environments Folder View', () => {
preloadFixtures('static/environments/environments_folder_view.html.raw'); preloadFixtures('static/environments/environments_folder_view.html.raw');
@ -36,6 +37,8 @@ describe('Environments Folder View', () => {
beforeEach(() => { beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor); Vue.http.interceptors.push(environmentsResponseInterceptor);
Vue.http.interceptors.push(headersInterceptor);
component = new EnvironmentsFolderViewComponent({ component = new EnvironmentsFolderViewComponent({
el: document.querySelector('#environments-folder-list-view'), el: document.querySelector('#environments-folder-list-view'),
}); });
@ -45,6 +48,7 @@ describe('Environments Folder View', () => {
Vue.http.interceptors = _.without( Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor, Vue.http.interceptors, environmentsResponseInterceptor,
); );
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
}); });
it('should render a table with environments', (done) => { it('should render a table with environments', (done) => {

View file

@ -0,0 +1,11 @@
// eslint-disable-next-line import/prefer-default-export
export const headersInterceptor = (request, next) => {
next((response) => {
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
// eslint-disable-next-line no-param-reassign
response.headers = headers;
});
};

View file

@ -3,7 +3,6 @@ import '~/render_math';
import '~/render_gfm'; import '~/render_gfm';
import issuableApp from '~/issue_show/components/app.vue'; import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub'; import eventHub from '~/issue_show/event_hub';
import Poll from '~/lib/utils/poll';
import issueShowData from '../mock_data'; import issueShowData from '../mock_data';
function formatText(text) { function formatText(text) {
@ -11,16 +10,26 @@ function formatText(text) {
} }
describe('Issuable output', () => { describe('Issuable output', () => {
let requestData = issueShowData.initialRequest;
document.body.innerHTML = '<span id="task_status"></span>'; document.body.innerHTML = '<span id="task_status"></span>';
const interceptor = (request, next) => {
next(request.respondWith(JSON.stringify(requestData), {
status: 200,
}));
};
let vm; let vm;
beforeEach(() => { beforeEach((done) => {
spyOn(eventHub, '$emit'); spyOn(eventHub, '$emit');
spyOn(Poll.prototype, 'makeRequest');
const IssuableDescriptionComponent = Vue.extend(issuableApp); const IssuableDescriptionComponent = Vue.extend(issuableApp);
requestData = issueShowData.initialRequest;
Vue.http.interceptors.push(interceptor);
vm = new IssuableDescriptionComponent({ vm = new IssuableDescriptionComponent({
propsData: { propsData: {
canUpdate: true, canUpdate: true,
@ -40,15 +49,17 @@ describe('Issuable output', () => {
projectPath: '/', projectPath: '/',
}, },
}).$mount(); }).$mount();
setTimeout(done);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
vm.poll.stop();
}); });
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', (done) => {
vm.poll.options.successCallback({
json() {
return issueShowData.initialRequest;
},
});
let editedText; let editedText;
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
@ -64,13 +75,10 @@ describe('Issuable output', () => {
expect(editedText.querySelector('time')).toBeTruthy(); expect(editedText.querySelector('time')).toBeTruthy();
}) })
.then(() => { .then(() => {
vm.poll.options.successCallback({ requestData = issueShowData.secondRequest;
json() { vm.poll.makeRequest();
return issueShowData.secondRequest;
},
});
}) })
.then(Vue.nextTick) .then(() => new Promise(resolve => setTimeout(resolve)))
.then(() => { .then(() => {
expect(document.querySelector('title').innerText).toContain('2 (#1)'); expect(document.querySelector('title').innerText).toContain('2 (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>'); expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
@ -304,7 +312,7 @@ describe('Issuable output', () => {
it('stops polling when deleting', (done) => { it('stops polling when deleting', (done) => {
spyOn(gl.utils, 'visitUrl'); spyOn(gl.utils, 'visitUrl');
spyOn(vm.poll, 'stop'); spyOn(vm.poll, 'stop').and.callThrough();
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => { spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({ resolve({
json() { json() {
@ -347,23 +355,14 @@ describe('Issuable output', () => {
describe('open form', () => { describe('open form', () => {
it('shows locked warning if form is open & data is different', (done) => { it('shows locked warning if form is open & data is different', (done) => {
vm.poll.options.successCallback({
json() {
return issueShowData.initialRequest;
},
});
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
vm.openForm(); vm.openForm();
vm.poll.options.successCallback({ requestData = issueShowData.secondRequest;
json() { vm.poll.makeRequest();
return issueShowData.secondRequest;
},
});
}) })
.then(Vue.nextTick) .then(() => new Promise(resolve => setTimeout(resolve)))
.then(() => { .then(() => {
expect( expect(
vm.formState.lockedWarningVisible, vm.formState.lockedWarningVisible,

102
yarn.lock
View file

@ -1575,6 +1575,12 @@ deckar01-task_list@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9" resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9"
decompress-response@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
dependencies:
mimic-response "^1.0.0"
deep-extend@~0.4.0: deep-extend@~0.4.0:
version "0.4.1" version "0.4.1"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
@ -1712,6 +1718,10 @@ dropzone@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3" resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3"
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
duplexer@^0.1.1, duplexer@~0.1.1: duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@ -2445,6 +2455,10 @@ get-caller-file@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
getpass@^0.1.1: getpass@^0.1.1:
version "0.1.6" version "0.1.6"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6"
@ -2521,6 +2535,25 @@ got@^3.2.0:
read-all-stream "^3.0.0" read-all-stream "^3.0.0"
timed-out "^2.0.0" timed-out "^2.0.0"
got@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
dependencies:
decompress-response "^3.2.0"
duplexer3 "^0.1.4"
get-stream "^3.0.0"
is-plain-obj "^1.1.0"
is-retry-allowed "^1.0.0"
is-stream "^1.0.0"
isurl "^1.0.0-alpha5"
lowercase-keys "^1.0.0"
p-cancelable "^0.3.0"
p-timeout "^1.1.1"
safe-buffer "^5.0.1"
timed-out "^4.0.0"
url-parse-lax "^1.0.0"
url-to-options "^1.0.1"
graceful-fs@^4.1.11, graceful-fs@^4.1.2: graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.1.11" version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@ -2578,6 +2611,16 @@ has-flag@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
has-symbol-support-x@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.3.0.tgz#588bd6927eaa0e296afae24160659167fc2be4f8"
has-to-string-tag-x@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.3.0.tgz#78e3d98c3c0ec9413e970eb8d766249a1e13058f"
dependencies:
has-symbol-support-x "^1.3.0"
has-unicode@^2.0.0: has-unicode@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@ -2902,6 +2945,10 @@ is-number@^2.0.2, is-number@^2.1.0:
dependencies: dependencies:
kind-of "^3.0.2" kind-of "^3.0.2"
is-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
is-path-cwd@^1.0.0: is-path-cwd@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
@ -2918,7 +2965,7 @@ is-path-inside@^1.0.0:
dependencies: dependencies:
path-is-inside "^1.0.1" path-is-inside "^1.0.1"
is-plain-obj@^1.0.0: is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
@ -2950,6 +2997,10 @@ is-resolvable@^1.0.0:
dependencies: dependencies:
tryit "^1.0.1" tryit "^1.0.1"
is-retry-allowed@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
is-stream@^1.0.0: is-stream@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@ -3087,6 +3138,13 @@ istanbul@^0.4.5:
which "^1.1.1" which "^1.1.1"
wordwrap "^1.0.0" wordwrap "^1.0.0"
isurl@^1.0.0-alpha5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
dependencies:
has-to-string-tag-x "^1.2.0"
is-object "^1.0.1"
jasmine-core@^2.6.3: jasmine-core@^2.6.3:
version "2.6.3" version "2.6.3"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.3.tgz#45072950e4a42b1e322fe55c001100a465d77815" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.3.tgz#45072950e4a42b1e322fe55c001100a465d77815"
@ -3633,6 +3691,10 @@ mime@1.3.4, mime@1.3.x, mime@^1.3.4:
version "1.3.4" version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
mimic-response@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e"
minimalistic-assert@^1.0.0: minimalistic-assert@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@ -3981,6 +4043,14 @@ osenv@^0.1.0:
os-homedir "^1.0.0" os-homedir "^1.0.0"
os-tmpdir "^1.0.0" os-tmpdir "^1.0.0"
p-cancelable@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
p-limit@^1.1.0: p-limit@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
@ -3991,6 +4061,12 @@ p-locate@^2.0.0:
dependencies: dependencies:
p-limit "^1.1.0" p-limit "^1.1.0"
p-timeout@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.0.tgz#9820f99434c5817868b4f34809ee5291660d5b6c"
dependencies:
p-finally "^1.0.0"
package-json@^1.0.0: package-json@^1.0.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0"
@ -4419,7 +4495,7 @@ prelude-ls@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
prepend-http@^1.0.0: prepend-http@^1.0.0, prepend-http@^1.0.1:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
@ -5384,6 +5460,10 @@ timed-out@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a"
timed-out@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
timers-browserify@^1.4.2: timers-browserify@^1.4.2:
version "1.4.2" version "1.4.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
@ -5545,6 +5625,12 @@ url-loader@^0.5.8:
loader-utils "^1.0.2" loader-utils "^1.0.2"
mime "1.3.x" mime "1.3.x"
url-parse-lax@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
dependencies:
prepend-http "^1.0.1"
url-parse@1.0.x: url-parse@1.0.x:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b"
@ -5559,6 +5645,10 @@ url-parse@^1.0.1, url-parse@^1.1.1:
querystringify "0.0.x" querystringify "0.0.x"
requires-port "1.0.x" requires-port "1.0.x"
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
url@^0.11.0: url@^0.11.0:
version "0.11.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@ -5657,9 +5747,11 @@ vue-loader@^11.3.4:
vue-style-loader "^2.0.0" vue-style-loader "^2.0.0"
vue-template-es2015-compiler "^1.2.2" vue-template-es2015-compiler "^1.2.2"
vue-resource@^0.9.3: vue-resource@^1.3.4:
version "0.9.3" version "1.3.4"
resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d" resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.4.tgz#9fc0bdf6a2f5cab430129fc99d347b3deae7b099"
dependencies:
got "^7.0.0"
vue-style-loader@^2.0.0: vue-style-loader@^2.0.0:
version "2.0.5" version "2.0.5"