summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Hanzel <mhanzel@gitlab.com>2019-07-18 08:46:36 +0200
committerPaul Slaughter <pslaughter@gitlab.com>2019-09-05 05:11:19 -0500
commit4d671f4d69d9bc59cdd51c140d0b031f6095094d (patch)
tree809930a1957f37cc6ff41f27d77c7ee7533680d6
parent4e9c531a84c012d2947e39d483531895d02208ea (diff)
downloadgitlab-ce-mh/wait-for-requests.tar.gz
Add helpers to wait for axios requestsmh/wait-for-requests
Add two methods to the axios_utils Jest mock: - `waitFor(url)`, which returns a Promise that resolves when the next request to `url` finishes. - `waitForAll()`, which returns a Promise that resolves when all pending requests finish.
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js11
-rw-r--r--spec/frontend/lib/utils/axios_utils_spec.js45
-rw-r--r--spec/frontend/mocks/ce/lib/utils/axios_utils.js62
-rw-r--r--spec/frontend/notes/old_notes_spec.js9
4 files changed, 113 insertions, 14 deletions
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 69159e2d741..37721cd030c 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -10,21 +10,18 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.interceptors.request.use(config => {
window.activeVueResources = window.activeVueResources || 0;
window.activeVueResources += 1;
-
return config;
});
// Remove the global counter
axios.interceptors.response.use(
- config => {
+ response => {
window.activeVueResources -= 1;
-
- return config;
+ return response;
},
- e => {
+ err => {
window.activeVueResources -= 1;
-
- return Promise.reject(e);
+ return Promise.reject(err);
},
);
diff --git a/spec/frontend/lib/utils/axios_utils_spec.js b/spec/frontend/lib/utils/axios_utils_spec.js
new file mode 100644
index 00000000000..d5c39567f06
--- /dev/null
+++ b/spec/frontend/lib/utils/axios_utils_spec.js
@@ -0,0 +1,45 @@
+/* eslint-disable promise/catch-or-return */
+
+import AxiosMockAdapter from 'axios-mock-adapter';
+
+import axios from '~/lib/utils/axios_utils';
+
+describe('axios_utils', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new AxiosMockAdapter(axios);
+ mock.onAny('/ok').reply(200);
+ mock.onAny('/err').reply(500);
+ expect(axios.countActiveRequests()).toBe(0);
+ });
+
+ afterEach(() => axios.waitForAll().finally(() => mock.restore()));
+
+ describe('waitForAll', () => {
+ it('resolves if there are no requests', () => axios.waitForAll());
+
+ it('waits for all requests to finish', () => {
+ const handler = jest.fn();
+ axios.get('/ok').then(handler);
+ axios.get('/err').catch(handler);
+
+ return axios.waitForAll().finally(() => {
+ expect(handler).toHaveBeenCalledTimes(2);
+ expect(handler.mock.calls[0][0].status).toBe(200);
+ expect(handler.mock.calls[1][0].response.status).toBe(500);
+ });
+ });
+ });
+
+ describe('waitFor', () => {
+ it('waits for requests on a specific URL', () => {
+ const handler = jest.fn();
+ axios.get('/ok').finally(handler);
+ axios.waitFor('/err').finally(() => {
+ throw new Error('waitFor on /err should not be called');
+ });
+ return axios.waitFor('/ok');
+ });
+ });
+});
diff --git a/spec/frontend/mocks/ce/lib/utils/axios_utils.js b/spec/frontend/mocks/ce/lib/utils/axios_utils.js
index a3783b91f95..85fad231d28 100644
--- a/spec/frontend/mocks/ce/lib/utils/axios_utils.js
+++ b/spec/frontend/mocks/ce/lib/utils/axios_utils.js
@@ -1,3 +1,5 @@
+import EventEmitter from 'events';
+
const axios = jest.requireActual('~/lib/utils/axios_utils').default;
axios.isMock = true;
@@ -13,4 +15,64 @@ axios.defaults.adapter = config => {
throw error;
};
+// Count active requests and provide a way to wait for them
+let activeRequests = 0;
+const events = new EventEmitter();
+const onRequest = () => {
+ activeRequests += 1;
+};
+
+// Use setImmediate to alloow the response interceptor to finish
+const onResponse = config => {
+ activeRequests -= 1;
+ setImmediate(() => {
+ events.emit('response', config);
+ });
+};
+
+const subscribeToResponse = (predicate = () => true) =>
+ new Promise(resolve => {
+ const listener = (config = {}) => {
+ if (predicate(config)) {
+ events.off('response', listener);
+ resolve(config);
+ }
+ };
+
+ events.on('response', listener);
+
+ // If a request has been made synchronously, setImmediate waits for it to be
+ // processed and the counter incremented.
+ setImmediate(listener);
+ });
+
+/**
+ * Registers a callback function to be run after a request to the given URL finishes.
+ */
+axios.waitFor = url => subscribeToResponse(({ url: configUrl }) => configUrl === url);
+
+/**
+ * Registers a callback function to be run after all requests have finished. If there are no requests waiting, the callback is executed immediately.
+ */
+axios.waitForAll = () => subscribeToResponse(() => activeRequests === 0);
+
+axios.countActiveRequests = () => activeRequests;
+
+axios.interceptors.request.use(config => {
+ onRequest();
+ return config;
+});
+
+// Remove the global counter
+axios.interceptors.response.use(
+ response => {
+ onResponse(response.config);
+ return response;
+ },
+ err => {
+ onResponse(err.config);
+ return Promise.reject(err);
+ },
+);
+
export default axios;
diff --git a/spec/frontend/notes/old_notes_spec.js b/spec/frontend/notes/old_notes_spec.js
index b57041cf4d1..96133c601aa 100644
--- a/spec/frontend/notes/old_notes_spec.js
+++ b/spec/frontend/notes/old_notes_spec.js
@@ -49,17 +49,12 @@ describe('Old Notes (~/notes.js)', () => {
setTestTimeoutOnce(4000);
});
- afterEach(done => {
+ afterEach(() => {
// The Notes component sets a polling interval. Clear it after every run.
// Make sure to use jest.runOnlyPendingTimers() instead of runAllTimers().
jest.clearAllTimers();
- setImmediate(() => {
- // Wait for any requests to resolve, otherwise we get failures about
- // unmocked requests.
- mockAxios.restore();
- done();
- });
+ return axios.waitForAll().finally(() => mockAxios.restore());
});
it('loads the Notes class into the DOM', () => {