Commit 3ab72627 authored by Clement Ho's avatar Clement Ho

Merge branch 'nfriend-suppress-ajax-errors-again' into 'master'

Suppress AJAX errors... again

Closes #32716

See merge request gitlab-org/gitlab!17706
parents d2eeeda0 2f591b3d
import axios from 'axios'; import axios from 'axios';
import csrf from './csrf'; import csrf from './csrf';
import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation';
axios.defaults.headers.common[csrf.headerKey] = csrf.token; axios.defaults.headers.common[csrf.headerKey] = csrf.token;
// Used by Rails to check if it is a valid XHR request // Used by Rails to check if it is a valid XHR request
...@@ -25,6 +26,20 @@ axios.interceptors.response.use( ...@@ -25,6 +26,20 @@ axios.interceptors.response.use(
}, },
); );
let isUserNavigating = false;
window.addEventListener('beforeunload', () => {
isUserNavigating = true;
});
// Ignore AJAX errors caused by requests
// being cancelled due to browser navigation
const { gon } = window;
const featureFlagEnabled = gon && gon.features && gon.features.suppressAjaxNavigationErrors;
axios.interceptors.response.use(
response => response,
err => suppressAjaxErrorsDuringNavigation(err, isUserNavigating, featureFlagEnabled),
);
export default axios; export default axios;
/** /**
......
/**
* An Axios error interceptor that suppresses AJAX errors caused
* by the request being cancelled when the user navigates to a new page
*/
export default (err, isUserNavigating, featureFlagEnabled) => {
if (featureFlagEnabled && isUserNavigating && err.code === 'ECONNABORTED') {
// If the user is navigating away from the current page,
// prevent .then() and .catch() handlers from being
// called by returning a Promise that never resolves
return new Promise(() => {});
}
// The error is not related to browser navigation,
// so propagate the error
return Promise.reject(err);
};
---
title: Suppress error messages shown when navigating to a new page
merge_request: 17706
author:
type: fixed
...@@ -100,6 +100,8 @@ describe('ProductivityApp component', () => { ...@@ -100,6 +100,8 @@ describe('ProductivityApp component', () => {
describe('user has access to the group', () => { describe('user has access to the group', () => {
beforeEach(() => { beforeEach(() => {
wrapper.vm.$store.state.charts.charts[chartKeys.main].errorCode = null; wrapper.vm.$store.state.charts.charts[chartKeys.main].errorCode = null;
return wrapper.vm.$nextTick();
}); });
describe('when the main chart is loading', () => { describe('when the main chart is loading', () => {
......
...@@ -38,6 +38,10 @@ module Gitlab ...@@ -38,6 +38,10 @@ module Gitlab
gon.current_user_fullname = current_user.name gon.current_user_fullname = current_user.name
gon.current_user_avatar_url = current_user.avatar_url gon.current_user_avatar_url = current_user.avatar_url
end end
# Initialize gon.features with any flags that should be
# made globally available to the frontend
push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true)
end end
# Exposes the state of a feature flag to the frontend code. # Exposes the state of a feature flag to the frontend code.
......
import suppressAjaxErrorsDuringNavigation from '~/lib/utils/suppress_ajax_errors_during_navigation';
import waitForPromises from 'helpers/wait_for_promises';
describe('suppressAjaxErrorsDuringNavigation', () => {
const OTHER_ERR_CODE = 'foo';
const NAV_ERR_CODE = 'ECONNABORTED';
it.each`
isFeatureFlagEnabled | isUserNavigating | code
${false} | ${false} | ${OTHER_ERR_CODE}
${false} | ${false} | ${NAV_ERR_CODE}
${false} | ${true} | ${OTHER_ERR_CODE}
${false} | ${true} | ${NAV_ERR_CODE}
${true} | ${false} | ${OTHER_ERR_CODE}
${true} | ${false} | ${NAV_ERR_CODE}
${true} | ${true} | ${OTHER_ERR_CODE}
`('should return a rejected Promise', ({ isFeatureFlagEnabled, isUserNavigating, code }) => {
const err = { code };
const actual = suppressAjaxErrorsDuringNavigation(err, isUserNavigating, isFeatureFlagEnabled);
return expect(actual).rejects.toBe(err);
});
it('should return a Promise that never resolves', () => {
const err = { code: NAV_ERR_CODE };
const actual = suppressAjaxErrorsDuringNavigation(err, true, true);
const thenCallback = jest.fn();
const catchCallback = jest.fn();
actual.then(thenCallback).catch(catchCallback);
return waitForPromises().then(() => {
expect(thenCallback).not.toHaveBeenCalled();
expect(catchCallback).not.toHaveBeenCalled();
});
});
});
...@@ -236,8 +236,15 @@ describe('Frequent Items App Component', () => { ...@@ -236,8 +236,15 @@ describe('Frequent Items App Component', () => {
.then(() => { .then(() => {
expect(vm.$el.querySelector('.loading-animation')).toBeDefined(); expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
}) })
// This test waits for multiple ticks in order to allow the responses to
// propagate through each interceptor installed on the Axios instance.
// This shouldn't be necessary; this test should be refactored to avoid this.
// https://gitlab.com/gitlab-org/gitlab/issues/32479
.then(vm.$nextTick)
.then(vm.$nextTick) .then(vm.$nextTick)
.then(vm.$nextTick) .then(vm.$nextTick)
.then(() => { .then(() => {
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe( expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
mockSearchedProjects.length, mockSearchedProjects.length,
......
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