Merge branch 'mh/wait-for-requests' into 'master'
Add helper to wait for axios requests in frontend tests Closes #60972 See merge request gitlab-org/gitlab-ce!30887
This commit is contained in:
commit
95ef272539
4 changed files with 113 additions and 14 deletions
|
@ -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);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
45
spec/frontend/lib/utils/axios_utils_spec.js
Normal file
45
spec/frontend/lib/utils/axios_utils_spec.js
Normal file
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
Loading…
Reference in a new issue