summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-07-12 14:47:09 +0000
committerPhil Hughes <me@iamphill.com>2017-07-12 14:47:09 +0000
commitaaa78199c2408a05d81aa4ee3058e81bb732da16 (patch)
tree97146599b47da2f86a1701c6012f60103bad9352
parentfd692d10663e5d3c7dc0de9221d6231b7fbcc464 (diff)
downloadgitlab-ce-aaa78199c2408a05d81aa4ee3058e81bb732da16.tar.gz
Update vue-resource
-rw-r--r--app/assets/javascripts/blob/notebook/index.js3
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js6
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js5
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js6
-rw-r--r--app/assets/javascripts/boards/models/list.js13
-rw-r--r--app/assets/javascripts/boards/services/board_service.js5
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue10
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js22
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js26
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js18
-rw-r--r--app/assets/javascripts/groups/index.js13
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue5
-rw-r--r--app/assets/javascripts/jobs/job_details_mediator.js3
-rw-r--r--app/assets/javascripts/notes.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue13
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue5
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediatior.js8
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue5
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js19
-rw-r--r--changelogs/unreleased/34534-update-vue-resource.yml4
-rw-r--r--doc/development/fe_guide/vue.md119
-rw-r--r--package.json2
-rw-r--r--spec/javascripts/environments/environment_spec.js9
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js4
-rw-r--r--spec/javascripts/helpers/vue_resource_helper.js11
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js55
-rw-r--r--yarn.lock102
28 files changed, 327 insertions, 170 deletions
diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js
index 36fe8a7184f..27312d718b0 100644
--- a/app/assets/javascripts/blob/notebook/index.js
+++ b/app/assets/javascripts/blob/notebook/index.js
@@ -51,8 +51,9 @@ export default () => {
methods: {
loadFile() {
this.$http.get(el.dataset.endpoint)
+ .then(response => response.json())
.then((res) => {
- this.json = res.json();
+ this.json = res;
this.loading = false;
})
.catch((e) => {
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index b94009ee76b..88b054b76e6 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -81,8 +81,9 @@ $(() => {
mounted () {
Store.disabled = this.disabled;
gl.boardService.all()
+ .then(response => response.json())
.then((resp) => {
- resp.json().forEach((board) => {
+ resp.forEach((board) => {
const list = Store.addList(board, this.defaultAvatar);
if (list.type === 'closed') {
@@ -97,7 +98,8 @@ $(() => {
Store.addBlankState();
this.loading = false;
- }).catch(() => new Flash('An error occurred. Please try again.'));
+ })
+ .catch(() => new Flash('An error occurred. Please try again.'));
},
methods: {
updateTokens() {
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js
index 870e115bd1a..e7f16899362 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js
+++ b/app/assets/javascripts/boards/components/board_blank_state.js
@@ -64,8 +64,9 @@ export default {
// Save the labels
gl.boardService.generateDefaultLists()
- .then((resp) => {
- resp.json().forEach((listObj) => {
+ .then(resp => resp.json())
+ .then((data) => {
+ data.forEach((listObj) => {
const list = Store.findList('title', listObj.title);
list.id = listObj.id;
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 6356c266ee2..1d36519c75c 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
return gl.boardService.getBacklog(queryData(this.filter.path, {
page: this.page,
per: this.perPage,
- })).then((res) => {
- const data = res.json();
-
+ }))
+ .then(resp => resp.json())
+ .then((data) => {
if (clearIssues) {
this.issues = [];
}
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index b4b09b3876e..08f7c5ddcd2 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -40,9 +40,8 @@ class List {
save () {
return gl.boardService.createList(this.label.id)
- .then((resp) => {
- const data = resp.json();
-
+ .then(resp => resp.json())
+ .then((data) => {
this.id = data.id;
this.type = data.list_type;
this.position = data.position;
@@ -91,8 +90,8 @@ class List {
}
return gl.boardService.getIssuesForList(this.id, data)
- .then((resp) => {
- const data = resp.json();
+ .then(resp => resp.json())
+ .then((data) => {
this.loading = false;
this.issuesSize = data.size;
@@ -109,8 +108,8 @@ class List {
this.issuesSize += 1;
return gl.boardService.newIssue(this.id, issue)
- .then((resp) => {
- const data = resp.json();
+ .then(resp => resp.json())
+ .then((data) => {
issue.id = data.iid;
if (this.issuesSize > 1) {
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index db9bced2f89..3742507b236 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -23,11 +23,6 @@ class BoardService {
url: bulkUpdatePath,
},
});
-
- Vue.http.interceptors.push((request, next) => {
- request.headers['X-CSRF-Token'] = $.rails.csrfToken();
- next();
- });
}
all () {
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 3c77f14d533..6d31b78b36d 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -51,11 +51,11 @@
},
methods: {
successCallback(resp) {
- const response = resp.json();
-
- // depending of the endpoint the response can either bring a `pipelines` key or not.
- const pipelines = response.pipelines || response;
- this.setCommonData(pipelines);
+ return resp.json().then((response) => {
+ // depending of the endpoint the response can either bring a `pipelines` key or not.
+ const pipelines = response.pipelines || response;
+ this.setCommonData(pipelines);
+ });
},
},
};
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js
index 9d51fb53eb2..efb6ced9f46 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js
@@ -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 ResolveService */
/* global Flash */
@@ -64,8 +64,6 @@ const ResolveBtn = Vue.extend({
});
},
resolve: function () {
- const errorFlashMsg = 'An error occurred when trying to resolve a comment. Please try again.';
-
if (!this.canResolve) return;
let promise;
@@ -79,24 +77,20 @@ const ResolveBtn = Vue.extend({
.resolve(this.noteId);
}
- promise.then((response) => {
- this.loading = false;
+ promise
+ .then(resp => resp.json())
+ .then((data) => {
+ this.loading = false;
- if (response.status === 200) {
- const data = response.json();
const resolved_by = data ? data.resolved_by : null;
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
this.discussion.updateHeadline(data);
gl.mrWidget.checkStatus();
- } else {
- new Flash(errorFlashMsg);
- }
- this.updateTooltip();
- }).catch(() => {
- new Flash(errorFlashMsg);
- });
+ this.updateTooltip();
+ })
+ .catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.'));
}
},
mounted: function () {
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 807ab11d292..2f063f6fe1f 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -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 CommentsStore */
@@ -32,27 +31,22 @@ class ResolveServiceClass {
promise = this.resolveAll(mergeRequestId, discussionId);
}
- promise.then((response) => {
- discussion.loading = false;
-
- if (response.status === 200) {
- const data = response.json();
- const resolved_by = data ? data.resolved_by : null;
+ promise
+ .then(resp => resp.json())
+ .then((data) => {
+ discussion.loading = false;
+ const resolvedBy = data ? data.resolved_by : null;
if (isResolved) {
discussion.unResolveAllNotes();
} else {
- discussion.resolveAllNotes(resolved_by);
+ discussion.resolveAllNotes(resolvedBy);
}
gl.mrWidget.checkStatus();
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) {
@@ -62,7 +56,7 @@ class ResolveServiceClass {
return this.discussionResource.save({
mergeRequestId,
- discussionId
+ discussionId,
}, {});
}
@@ -73,7 +67,7 @@ class ResolveServiceClass {
return this.discussionResource.delete({
mergeRequestId,
- discussionId
+ discussionId,
}, {});
}
}
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index 25b24fbd6dc..8f4066e3a6e 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -1,17 +1,15 @@
export default {
methods: {
saveData(resp) {
- const response = {
- headers: resp.headers,
- body: resp.json(),
- };
+ const headers = resp.headers;
+ return resp.json().then((response) => {
+ this.isLoading = false;
- this.isLoading = false;
-
- this.store.storeAvailableCount(response.body.available_count);
- this.store.storeStoppedCount(response.body.stopped_count);
- this.store.storeEnvironments(response.body.environments);
- this.store.setPagination(response.headers);
+ this.store.storeAvailableCount(response.available_count);
+ this.store.storeStoppedCount(response.stopped_count);
+ this.store.storeEnvironments(response.environments);
+ this.store.setPagination(headers);
+ });
},
},
};
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index ff601db2aa6..00e1bd94c9c 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -99,8 +99,10 @@ document.addEventListener('DOMContentLoaded', () => {
page: currentPath,
}, document.title, currentPath);
- this.updateGroups(response.json());
- this.updatePagination(response.headers);
+ return response.json().then((data) => {
+ this.updateGroups(data);
+ this.updatePagination(response.headers);
+ });
})
.catch(this.handleErrorResponse);
},
@@ -114,18 +116,19 @@ document.addEventListener('DOMContentLoaded', () => {
},
leaveGroup(group, collection) {
this.service.leaveGroup(group.leavePath)
+ .then(resp => resp.json())
.then((response) => {
$.scrollTo(0);
this.store.removeGroup(group, collection);
// 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.';
- if (response.status === 403) {
+ if (error.status === 403) {
message = 'Failed to leave the group. Please make sure you are not the only owner';
}
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 3d5fb7f441c..efae112923d 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -202,10 +202,7 @@ export default {
this.poll = new Poll({
resource: this.service,
method: 'getData',
- successCallback: (res) => {
- const data = res.json();
- this.store.updateState(data);
- },
+ successCallback: res => res.json().then(data => this.store.updateState(data)),
errorCallback(err) {
throw new Error(err);
},
diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js
index 063c52fac74..cc014b815c4 100644
--- a/app/assets/javascripts/jobs/job_details_mediator.js
+++ b/app/assets/javascripts/jobs/job_details_mediator.js
@@ -54,9 +54,8 @@ export default class JobMediator {
}
successCallback(response) {
- const data = response.json();
this.state.isLoading = false;
- this.store.storeJob(data);
+ return response.json().then(data => this.store.storeJob(data));
}
errorCallback() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 1a68c5bca00..b2c503d1656 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1270,7 +1270,7 @@ export default class Notes {
<div class="timeline-entry-inner">
<div class="timeline-icon">
<a href="/${currentUsername}">
- <img class="avatar s40" src="${currentUserAvatar}">
+ <img class="avatar s40" src="${currentUserAvatar}" />
</a>
</div>
<div class="timeline-content ${discussionClass}">
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index 01ae07aad65..5df317a76bf 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -129,14 +129,11 @@
},
successCallback(resp) {
- const response = {
- headers: resp.headers,
- body: resp.json(),
- };
-
- this.store.storeCount(response.body.count);
- this.store.storePagination(response.headers);
- this.setCommonData(response.body.pipelines);
+ return resp.json().then((response) => {
+ this.store.storeCount(response.count);
+ this.store.storePagination(resp.headers);
+ this.setCommonData(response.pipelines);
+ });
},
},
};
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 87b2725a045..a4a27247406 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -73,8 +73,9 @@ export default {
fetchJobs() {
this.$http.get(this.stage.dropdown_path)
- .then((response) => {
- this.dropdownContent = response.json().html;
+ .then(response => response.json())
+ .then((data) => {
+ this.dropdownContent = data.html;
this.isLoading = false;
})
.catch(() => {
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
index 82537ea06f5..385e7430a7d 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
@@ -40,10 +40,10 @@ export default class pipelinesMediator {
}
successCallback(response) {
- const data = response.json();
-
- this.state.isLoading = false;
- this.store.storePipeline(data);
+ return response.json().then((data) => {
+ this.state.isLoading = false;
+ this.store.storePipeline(data);
+ });
}
errorCallback() {
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 5ccfb4ee9c1..721e92221cf 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -28,8 +28,8 @@ export default class SidebarMediator {
fetch() {
this.service.get()
- .then((response) => {
- const data = response.json();
+ .then(response => response.json())
+ .then((data) => {
this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data);
})
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 8303c556f64..4e10bbc7408 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -44,9 +44,8 @@
text: this.$slots.textarea[0].elm.value,
},
)
- .then((res) => {
- const data = res.json();
-
+ .then(resp => resp.json())
+ .then((data) => {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body;
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
index 740930dce5b..7f8e514fda1 100644
--- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
+++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
@@ -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) => {
if ($.rails) {
- // eslint-disable-next-line no-param-reassign
- request.headers['X-CSRF-Token'] = $.rails.csrfToken();
+ request.headers.set('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;
+ });
});
diff --git a/changelogs/unreleased/34534-update-vue-resource.yml b/changelogs/unreleased/34534-update-vue-resource.yml
new file mode 100644
index 00000000000..2d0af0c9bfe
--- /dev/null
+++ b/changelogs/unreleased/34534-update-vue-resource.yml
@@ -0,0 +1,4 @@
+---
+title: Updates vue resource and code according to breaking changes
+merge_request:
+author:
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index a984bb6c94c..0742b202807 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -112,7 +112,50 @@ Vue Resource should only be imported in the service file.
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.
`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`
@@ -126,13 +169,13 @@ The following example shows an application:
// store.js
export default class Store {
- /**
+ /**
* This is where we will iniatialize the state of our data.
* Usually in a small SPA you don't need any options when starting the store. In the case you do
* need guarantee it's an Object and it's documented.
- *
- * @param {Object} options
- */
+ *
+ * @param {Object} options
+ */
constructor(options) {
this.options = options;
@@ -205,14 +248,14 @@ import Store from 'store';
import Service from 'service';
import TodoComponent from 'todoComponent';
export default {
- /**
+ /**
* Although most data belongs in the store, each component it's own state.
* We want to show a loading spinner while we are fetching the todos, this state belong
* in the component.
*
* We need to access the store methods through all methods of our component.
* We need to access the state of our store.
- */
+ */
data() {
const store = new Store();
@@ -396,42 +439,46 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e
[Vue Resource Interceptors][vue-resource-interceptor] allow us to add a interceptor with
the response we need:
-```javascript
- // Mock the service to return data
- const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify([{
- title: 'This is a todo',
- body: 'This is the text'
- }]), {
- status: 200,
- }));
- };
+ ```javascript
+ // Mock the service to return data
+ const interceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([{
+ title: 'This is a todo',
+ body: 'This is the text'
+ }]), {
+ status: 200,
+ }));
+ };
- beforeEach(() => {
- Vue.http.interceptors.push(interceptor);
- });
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
- afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
- });
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
- it('should do something', (done) => {
- setTimeout(() => {
- // Test received data
- done();
- }, 0);
- });
-```
+ it('should do something', (done) => {
+ setTimeout(() => {
+ // Test received data
+ done();
+ }, 0);
+ });
+ ```
+
+1. Headers interceptor
+Refer to [this section](vue.md#headers)
1. Use `$.mount()` to mount the component
+
```javascript
- // bad
- new Component({
- el: document.createElement('div')
- });
+// bad
+new Component({
+ el: document.createElement('div')
+});
- // good
- new Component().$mount();
+// good
+new Component().$mount();
```
[vue-docs]: http://vuejs.org/guide/index.html
diff --git a/package.json b/package.json
index 5a997e813f8..fd944531a6a 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
"visibilityjs": "^1.2.4",
"vue": "^2.2.6",
"vue-loader": "^11.3.4",
- "vue-resource": "^0.9.3",
+ "vue-resource": "^1.3.4",
"vue-template-compiler": "^2.2.6",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.8.2"
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index 6639a6b5e7b..0c8817a8148 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import '~/flash';
import environmentsComponent from '~/environments/components/environment.vue';
import { environment, folder } from './mock_data';
+import { headersInterceptor } from '../helpers/vue_resource_helper';
describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw');
@@ -25,12 +26,14 @@ describe('Environment', () => {
beforeEach(() => {
Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
+ Vue.http.interceptors.push(headersInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsEmptyResponseInterceptor,
);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
it('should render the empty state', (done) => {
@@ -54,6 +57,10 @@ describe('Environment', () => {
describe('with paginated environments', () => {
const environmentsResponseInterceptor = (request, next) => {
+ next((response) => {
+ response.headers.set('X-nExt-pAge', '2');
+ });
+
next(request.respondWith(JSON.stringify({
environments: [environment],
stopped_count: 1,
@@ -73,6 +80,7 @@ describe('Environment', () => {
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
+ Vue.http.interceptors.push(headersInterceptor);
component = new EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
});
@@ -82,6 +90,7 @@ describe('Environment', () => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor,
);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
it('should render a table with environments', (done) => {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 350078ad5f5..fdaea5c0b0c 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import '~/flash';
import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import { environmentsList } from '../mock_data';
+import { headersInterceptor } from '../../helpers/vue_resource_helper';
describe('Environments Folder View', () => {
preloadFixtures('static/environments/environments_folder_view.html.raw');
@@ -36,6 +37,8 @@ describe('Environments Folder View', () => {
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
+ Vue.http.interceptors.push(headersInterceptor);
+
component = new EnvironmentsFolderViewComponent({
el: document.querySelector('#environments-folder-list-view'),
});
@@ -45,6 +48,7 @@ describe('Environments Folder View', () => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor,
);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
it('should render a table with environments', (done) => {
diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js
new file mode 100644
index 00000000000..0d1bf5e2e80
--- /dev/null
+++ b/spec/javascripts/helpers/vue_resource_helper.js
@@ -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;
+ });
+};
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index bc13373a27e..81ce18bf2fb 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -3,7 +3,6 @@ import '~/render_math';
import '~/render_gfm';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
-import Poll from '~/lib/utils/poll';
import issueShowData from '../mock_data';
function formatText(text) {
@@ -11,16 +10,26 @@ function formatText(text) {
}
describe('Issuable output', () => {
+ let requestData = issueShowData.initialRequest;
+
document.body.innerHTML = '<span id="task_status"></span>';
+ const interceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify(requestData), {
+ status: 200,
+ }));
+ };
+
let vm;
- beforeEach(() => {
+ beforeEach((done) => {
spyOn(eventHub, '$emit');
- spyOn(Poll.prototype, 'makeRequest');
const IssuableDescriptionComponent = Vue.extend(issuableApp);
+ requestData = issueShowData.initialRequest;
+ Vue.http.interceptors.push(interceptor);
+
vm = new IssuableDescriptionComponent({
propsData: {
canUpdate: true,
@@ -40,15 +49,17 @@ describe('Issuable output', () => {
projectPath: '/',
},
}).$mount();
+
+ setTimeout(done);
});
- it('should render a title/description/edited and update title/description/edited on update', (done) => {
- vm.poll.options.successCallback({
- json() {
- return issueShowData.initialRequest;
- },
- });
+ 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) => {
let editedText;
Vue.nextTick()
.then(() => {
@@ -64,13 +75,10 @@ describe('Issuable output', () => {
expect(editedText.querySelector('time')).toBeTruthy();
})
.then(() => {
- vm.poll.options.successCallback({
- json() {
- return issueShowData.secondRequest;
- },
- });
+ requestData = issueShowData.secondRequest;
+ vm.poll.makeRequest();
})
- .then(Vue.nextTick)
+ .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>');
@@ -304,7 +312,7 @@ describe('Issuable output', () => {
it('stops polling when deleting', (done) => {
spyOn(gl.utils, 'visitUrl');
- spyOn(vm.poll, 'stop');
+ spyOn(vm.poll, 'stop').and.callThrough();
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
json() {
@@ -347,23 +355,14 @@ describe('Issuable output', () => {
describe('open form', () => {
it('shows locked warning if form is open & data is different', (done) => {
- vm.poll.options.successCallback({
- json() {
- return issueShowData.initialRequest;
- },
- });
-
Vue.nextTick()
.then(() => {
vm.openForm();
- vm.poll.options.successCallback({
- json() {
- return issueShowData.secondRequest;
- },
- });
+ requestData = issueShowData.secondRequest;
+ vm.poll.makeRequest();
})
- .then(Vue.nextTick)
+ .then(() => new Promise(resolve => setTimeout(resolve)))
.then(() => {
expect(
vm.formState.lockedWarningVisible,
diff --git a/yarn.lock b/yarn.lock
index b04eebe60af..98da6a984d1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1575,6 +1575,12 @@ deckar01-task_list@^2.0.0:
version "2.0.0"
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:
version "0.4.1"
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"
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:
version "0.1.1"
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"
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:
version "0.1.6"
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"
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:
version "4.1.11"
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"
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:
version "2.0.1"
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:
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:
version "1.0.0"
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:
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"
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:
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:
version "1.1.0"
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"
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:
version "2.6.3"
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"
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:
version "1.0.0"
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-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:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
@@ -3991,6 +4061,12 @@ p-locate@^2.0.0:
dependencies:
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:
version "1.2.0"
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"
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"
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"
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:
version "1.4.2"
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"
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:
version "1.0.5"
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"
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:
version "0.11.0"
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-template-es2015-compiler "^1.2.2"
-vue-resource@^0.9.3:
- version "0.9.3"
- resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d"
+vue-resource@^1.3.4:
+ version "1.3.4"
+ 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:
version "2.0.5"