From cb0bf3924543f08ed8af093ec8b08610d4a87bb9 Mon Sep 17 00:00:00 2001
From: Winnie Hellmann <winnie@gitlab.com>
Date: Tue, 17 Dec 2019 06:09:42 +0000
Subject: [PATCH] Add clearMocks Jest config

**Why?**
This removes the need to add `jest.clearAllMocks` manually.
Without this, it's possible for tests to have false positives
because the `mock.calls` is never reset.

**References:**
- https://jestjs.io/docs/en/configuration#clearmocks-boolean
- https://gitlab.com/gitlab-org/gitlab/merge_requests/21860
---
 .../frontend/monitoring/alert_widget_spec.js  |  1 -
 .../merge_requests/blocking_mr_input_spec.js  |  1 -
 .../components/project_manager_spec.js        |  1 -
 .../modules/project_selector/actions_spec.js  |  1 -
 .../vulnerable_projects/actions_spec.js       |  4 --
 .../accordion/accordion_item_spec.js          |  2 -
 .../components/accordion/accordion_spec.js    |  2 -
 .../dismissal_comment_modal_footer_spec.js    |  1 -
 jest.config.js                                |  1 +
 spec/frontend/boards/boards_store_spec.js     |  1 -
 .../frontend/clusters/clusters_bundle_spec.js |  1 -
 .../commit_pipeline_status_component_spec.js  |  1 -
 .../eks_cluster/store/actions_spec.js         | 11 ++---
 .../ide/components/preview/clientside_spec.js |  4 --
 .../stores/modules/pipelines/actions_spec.js  |  2 -
 .../components/issuables_list_app_spec.js     |  1 -
 spec/frontend/jest_self_check/mocks_spec.js   | 43 +++++++++++++++++++
 .../notes/components/comment_form_spec.js     |  1 -
 .../components/user_modal_manager_spec.js     |  4 --
 .../registry/list/components/app_spec.js      |  1 -
 .../repository/components/table/row_spec.js   |  1 -
 spec/frontend/sentry/sentry_config_spec.js    |  7 +--
 .../confidential_issue_sidebar_spec.js        |  1 -
 23 files changed, 49 insertions(+), 44 deletions(-)
 create mode 100644 spec/frontend/jest_self_check/mocks_spec.js

diff --git a/ee/spec/frontend/monitoring/alert_widget_spec.js b/ee/spec/frontend/monitoring/alert_widget_spec.js
index 12283d7b1a9f..903652a5e6de 100644
--- a/ee/spec/frontend/monitoring/alert_widget_spec.js
+++ b/ee/spec/frontend/monitoring/alert_widget_spec.js
@@ -62,7 +62,6 @@ describe('AlertWidget', () => {
   const findCurrentSettings = () => wrapper.find('.alert-current-setting');
 
   afterEach(() => {
-    jest.clearAllMocks();
     wrapper.destroy();
     wrapper = null;
   });
diff --git a/ee/spec/frontend/projects/merge_requests/blocking_mr_input_spec.js b/ee/spec/frontend/projects/merge_requests/blocking_mr_input_spec.js
index d23d11d15e7b..d09e877c443f 100644
--- a/ee/spec/frontend/projects/merge_requests/blocking_mr_input_spec.js
+++ b/ee/spec/frontend/projects/merge_requests/blocking_mr_input_spec.js
@@ -22,7 +22,6 @@ describe('BlockingMrInput', () => {
 
   afterEach(() => {
     document.querySelector('#test').remove();
-    jest.clearAllMocks();
   });
 
   it('adds hidden references block when hidden count is greater than 0', () => {
diff --git a/ee/spec/frontend/security_dashboard/components/project_manager_spec.js b/ee/spec/frontend/security_dashboard/components/project_manager_spec.js
index fdd745a34c00..96db4a4831e5 100644
--- a/ee/spec/frontend/security_dashboard/components/project_manager_spec.js
+++ b/ee/spec/frontend/security_dashboard/components/project_manager_spec.js
@@ -66,7 +66,6 @@ describe('Project Manager component', () => {
 
   afterEach(() => {
     wrapper.destroy();
-    jest.clearAllMocks();
   });
 
   describe('given the default state', () => {
diff --git a/ee/spec/frontend/security_dashboard/store/modules/project_selector/actions_spec.js b/ee/spec/frontend/security_dashboard/store/modules/project_selector/actions_spec.js
index aab263f23e30..86cc8b30c516 100644
--- a/ee/spec/frontend/security_dashboard/store/modules/project_selector/actions_spec.js
+++ b/ee/spec/frontend/security_dashboard/store/modules/project_selector/actions_spec.js
@@ -29,7 +29,6 @@ describe('projectSelector actions', () => {
   });
 
   afterEach(() => {
-    jest.clearAllMocks();
     mockAxios.restore();
   });
 
diff --git a/ee/spec/frontend/security_dashboard/store/modules/vulnerable_projects/actions_spec.js b/ee/spec/frontend/security_dashboard/store/modules/vulnerable_projects/actions_spec.js
index a1a8447c1b15..e85b66cb80f1 100644
--- a/ee/spec/frontend/security_dashboard/store/modules/vulnerable_projects/actions_spec.js
+++ b/ee/spec/frontend/security_dashboard/store/modules/vulnerable_projects/actions_spec.js
@@ -24,10 +24,6 @@ describe('Vulnerable Projects actions', () => {
     state = createState();
   });
 
-  afterEach(() => {
-    jest.clearAllMocks();
-  });
-
   describe('fetchProjects', () => {
     beforeEach(() => {
       mockAxios = new MockAdapter(axios);
diff --git a/ee/spec/frontend/vue_shared/components/accordion/accordion_item_spec.js b/ee/spec/frontend/vue_shared/components/accordion/accordion_item_spec.js
index 30ec29a5c224..6630d1459e54 100644
--- a/ee/spec/frontend/vue_shared/components/accordion/accordion_item_spec.js
+++ b/ee/spec/frontend/vue_shared/components/accordion/accordion_item_spec.js
@@ -54,8 +54,6 @@ describe('AccordionItem component', () => {
   afterEach(() => {
     wrapper.destroy();
     wrapper = null;
-
-    jest.clearAllMocks();
   });
 
   describe('rendering options', () => {
diff --git a/ee/spec/frontend/vue_shared/components/accordion/accordion_spec.js b/ee/spec/frontend/vue_shared/components/accordion/accordion_spec.js
index be6b892c6886..2f6655dae81a 100644
--- a/ee/spec/frontend/vue_shared/components/accordion/accordion_spec.js
+++ b/ee/spec/frontend/vue_shared/components/accordion/accordion_spec.js
@@ -22,8 +22,6 @@ describe('Accordion component', () => {
   afterEach(() => {
     wrapper.destroy();
     wrapper = null;
-
-    jest.clearAllMocks();
   });
 
   it('contains a default slot', () => {
diff --git a/ee/spec/frontend/vue_shared/security_reports/components/dismissal_comment_modal_footer_spec.js b/ee/spec/frontend/vue_shared/security_reports/components/dismissal_comment_modal_footer_spec.js
index 8ed5ec322d69..5bcd0a9e614f 100644
--- a/ee/spec/frontend/vue_shared/security_reports/components/dismissal_comment_modal_footer_spec.js
+++ b/ee/spec/frontend/vue_shared/security_reports/components/dismissal_comment_modal_footer_spec.js
@@ -11,7 +11,6 @@ describe('DismissalCommentModalFooter', () => {
 
   afterEach(() => {
     document.body.dataset.page = origPage;
-    jest.clearAllMocks();
     wrapper.destroy();
   });
 
diff --git a/jest.config.js b/jest.config.js
index 79522552c6d9..59e09c85b5ad 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -55,6 +55,7 @@ if (IS_EE) {
 
 // eslint-disable-next-line import/no-commonjs
 module.exports = {
+  clearMocks: true,
   testMatch,
   moduleFileExtensions: ['js', 'json', 'vue'],
   moduleNameMapper,
diff --git a/spec/frontend/boards/boards_store_spec.js b/spec/frontend/boards/boards_store_spec.js
index 3588197ebdc9..bf3d81d31178 100644
--- a/spec/frontend/boards/boards_store_spec.js
+++ b/spec/frontend/boards/boards_store_spec.js
@@ -41,7 +41,6 @@ describe('boardsStore', () => {
 
   afterEach(() => {
     axiosMock.restore();
-    jest.clearAllMocks();
   });
 
   const setupDefaultResponses = () => {
diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js
index 7b1d96c8da54..d7c648bcd208 100644
--- a/spec/frontend/clusters/clusters_bundle_spec.js
+++ b/spec/frontend/clusters/clusters_bundle_spec.js
@@ -46,7 +46,6 @@ describe('Clusters', () => {
   afterEach(() => {
     cluster.destroy();
     mock.restore();
-    jest.clearAllMocks();
   });
 
   describe('class constructor', () => {
diff --git a/spec/frontend/commit/commit_pipeline_status_component_spec.js b/spec/frontend/commit/commit_pipeline_status_component_spec.js
index 36fc6ee52a8a..a2a6d405eab3 100644
--- a/spec/frontend/commit/commit_pipeline_status_component_spec.js
+++ b/spec/frontend/commit/commit_pipeline_status_component_spec.js
@@ -44,7 +44,6 @@ describe('Commit pipeline status component', () => {
   afterEach(() => {
     wrapper.destroy();
     wrapper = null;
-    jest.clearAllMocks();
   });
 
   describe('Visibility management', () => {
diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
index fda1f71b1f90..1139f0947058 100644
--- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
@@ -272,14 +272,11 @@ describe('EKS Cluster Store Actions', () => {
       payload = { name: ['Create cluster failed'] };
     });
 
-    it('commits createClusterError mutation', () => {
+    it('commits createClusterError mutation and displays flash message', () =>
       testAction(actions.createClusterError, payload, state, [
         { type: CREATE_CLUSTER_ERROR, payload },
-      ]);
-    });
-
-    it('creates a flash that displays the create cluster error', () => {
-      expect(createFlash).toHaveBeenCalledWith(payload.name[0]);
-    });
+      ]).then(() => {
+        expect(createFlash).toHaveBeenCalledWith(payload.name[0]);
+      }));
   });
 });
diff --git a/spec/frontend/ide/components/preview/clientside_spec.js b/spec/frontend/ide/components/preview/clientside_spec.js
index 6a33f4998c50..5cb9e598fc42 100644
--- a/spec/frontend/ide/components/preview/clientside_spec.js
+++ b/spec/frontend/ide/components/preview/clientside_spec.js
@@ -68,10 +68,6 @@ describe('IDE clientside preview', () => {
     jest.useRealTimers();
   });
 
-  beforeEach(() => {
-    jest.clearAllMocks();
-  });
-
   afterEach(() => {
     wrapper.destroy();
   });
diff --git a/spec/frontend/ide/stores/modules/pipelines/actions_spec.js b/spec/frontend/ide/stores/modules/pipelines/actions_spec.js
index a58c7b8f8193..b08d1cd01da2 100644
--- a/spec/frontend/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/frontend/ide/stores/modules/pipelines/actions_spec.js
@@ -111,8 +111,6 @@ describe('IDE pipelines actions', () => {
   });
 
   describe('fetchLatestPipeline', () => {
-    beforeEach(() => {});
-
     afterEach(() => {
       stopPipelinePolling();
       clearEtagPoll();
diff --git a/spec/frontend/issuables_list/components/issuables_list_app_spec.js b/spec/frontend/issuables_list/components/issuables_list_app_spec.js
index 666ccc074164..621e8b8aa544 100644
--- a/spec/frontend/issuables_list/components/issuables_list_app_spec.js
+++ b/spec/frontend/issuables_list/components/issuables_list_app_spec.js
@@ -72,7 +72,6 @@ describe('Issuables list component', () => {
   afterEach(() => {
     wrapper.destroy();
     mockAxios.restore();
-    jest.clearAllMocks();
     window.location = oldLocation;
   });
 
diff --git a/spec/frontend/jest_self_check/mocks_spec.js b/spec/frontend/jest_self_check/mocks_spec.js
new file mode 100644
index 000000000000..f1e9e12e6330
--- /dev/null
+++ b/spec/frontend/jest_self_check/mocks_spec.js
@@ -0,0 +1,43 @@
+import * as textUtils from '~/lib/utils/text_utility';
+
+jest.mock('~/lib/utils/text_utility');
+
+describe('does restore mocks config work?', () => {
+  describe('shared spy', () => {
+    const spy = jest.fn();
+
+    beforeEach(() => {
+      spy();
+    });
+
+    it('is only called once', () => {
+      expect(spy).toHaveBeenCalledTimes(1);
+    });
+
+    it('is only called once B', () => {
+      expect(spy).toHaveBeenCalledTimes(1);
+    });
+
+    it('is only called once C', () => {
+      expect(spy).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe('module mock', () => {
+    beforeEach(() => {
+      textUtils.humanize('');
+    });
+
+    it('is only called once', () => {
+      expect(textUtils.humanize).toHaveBeenCalledTimes(1);
+    });
+
+    it('is only called once B', () => {
+      expect(textUtils.humanize).toHaveBeenCalledTimes(1);
+    });
+
+    it('is only called once C', () => {
+      expect(textUtils.humanize).toHaveBeenCalledTimes(1);
+    });
+  });
+});
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js
index 10d92e9535c0..7652f48474d5 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -50,7 +50,6 @@ describe('issue_comment_form component', () => {
   afterEach(() => {
     axiosMock.restore();
     wrapper.destroy();
-    jest.clearAllMocks();
   });
 
   describe('user is logged in', () => {
diff --git a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
index 7653fffc5024..c88a182660d7 100644
--- a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
+++ b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
@@ -83,10 +83,6 @@ describe('Users admin page Modal Manager', () => {
       jest.spyOn(document, 'removeEventListener');
     });
 
-    afterEach(() => {
-      jest.clearAllMocks();
-    });
-
     afterAll(() => {
       jest.restoreAllMocks();
     });
diff --git a/spec/frontend/registry/list/components/app_spec.js b/spec/frontend/registry/list/components/app_spec.js
index f2733ac9fefb..5072a285f835 100644
--- a/spec/frontend/registry/list/components/app_spec.js
+++ b/spec/frontend/registry/list/components/app_spec.js
@@ -52,7 +52,6 @@ describe('Registry List', () => {
   });
 
   afterEach(() => {
-    jest.clearAllMocks();
     Vue.config.silent = false;
     wrapper.destroy();
   });
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index aa0b9385f1ac..94fa8b1e3635 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -35,7 +35,6 @@ function factory(propsData = {}) {
 describe('Repository table row component', () => {
   afterEach(() => {
     vm.destroy();
-    jest.clearAllMocks();
   });
 
   it('renders table row', () => {
diff --git a/spec/frontend/sentry/sentry_config_spec.js b/spec/frontend/sentry/sentry_config_spec.js
index 62b8bbd50a2a..bcc7f29b98dd 100644
--- a/spec/frontend/sentry/sentry_config_spec.js
+++ b/spec/frontend/sentry/sentry_config_spec.js
@@ -54,8 +54,7 @@ describe('SentryConfig', () => {
     });
 
     it('should not call setUser if there is no current user ID', () => {
-      jest.clearAllMocks();
-
+      SentryConfig.setUser.mockClear();
       options.currentUserId = undefined;
 
       SentryConfig.init(options);
@@ -167,8 +166,6 @@ describe('SentryConfig', () => {
 
     describe('if no err is provided', () => {
       beforeEach(() => {
-        jest.clearAllMocks();
-
         SentryConfig.handleSentryErrors(event, req, config);
       });
 
@@ -191,8 +188,6 @@ describe('SentryConfig', () => {
       beforeEach(() => {
         req.responseText = undefined;
 
-        jest.clearAllMocks();
-
         SentryConfig.handleSentryErrors(event, req, config, err);
       });
 
diff --git a/spec/frontend/sidebar/confidential_issue_sidebar_spec.js b/spec/frontend/sidebar/confidential_issue_sidebar_spec.js
index 68dde14880ac..432ec111e523 100644
--- a/spec/frontend/sidebar/confidential_issue_sidebar_spec.js
+++ b/spec/frontend/sidebar/confidential_issue_sidebar_spec.js
@@ -42,7 +42,6 @@ describe('Confidential Issue Sidebar Block', () => {
   };
 
   beforeEach(() => {
-    jest.clearAllMocks();
     jest.spyOn(window.location, 'reload').mockImplementation();
   });
 
-- 
GitLab