diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 317fe6442d4286a5c9b9c346255e1ce83c5a1765..7eba491430b60cc3205b0a43fc6b667b3456c4e9 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -1,58 +1,10 @@
-import { ASC, MR_FILTER_OPTIONS } from '../../constants';
 import * as actions from '../actions';
 import * as getters from '../getters';
 import mutations from '../mutations';
+import createState from '../state';
 
 export default () => ({
-  state: {
-    discussions: [],
-    discussionSortOrder: ASC,
-    persistSortOrder: true,
-    convertedDisscussionIds: [],
-    targetNoteHash: null,
-    lastFetchedAt: null,
-    currentDiscussionId: null,
-    batchSuggestionsInfo: [],
-    currentlyFetchingDiscussions: false,
-    doneFetchingBatchDiscussions: false,
-    /**
-     * selectedCommentPosition & selectedCommentPositionHover structures are the same as `position.line_range`:
-     * {
-     *  start: { line_code: string, new_line: number, old_line:number, type: string },
-     *  end: { line_code: string, new_line: number, old_line:number, type: string },
-     * }
-     */
-    selectedCommentPosition: null,
-    selectedCommentPositionHover: null,
-
-    // View layer
-    isToggleStateButtonLoading: false,
-    isNotesFetched: false,
-    isLoading: true,
-    isLoadingDescriptionVersion: false,
-    isPromoteCommentToTimelineEventInProgress: false,
-
-    // holds endpoints and permissions provided through haml
-    notesData: {
-      markdownDocsPath: '',
-    },
-    userData: {},
-    noteableData: {
-      discussion_locked: false,
-      confidential: false, // TODO: Move data like this to Issue Store, should not be apart of notes.
-      current_user: {},
-      preview_note_path: 'path/to/preview',
-    },
-    isResolvingDiscussion: false,
-    commentsDisabled: false,
-    resolvableDiscussionsCount: 0,
-    unresolvedDiscussionsCount: 0,
-    descriptionVersions: {},
-    isTimelineEnabled: false,
-    isFetching: false,
-    isPollingInitialized: false,
-    mergeRequestFilters: MR_FILTER_OPTIONS.map((f) => f.value),
-  },
+  state: createState(),
   actions,
   getters,
   mutations,
diff --git a/app/assets/javascripts/notes/stores/state.js b/app/assets/javascripts/notes/stores/state.js
new file mode 100644
index 0000000000000000000000000000000000000000..8e49cd861a1f36469315a69bd579c4ef152c9d34
--- /dev/null
+++ b/app/assets/javascripts/notes/stores/state.js
@@ -0,0 +1,53 @@
+import { ASC, MR_FILTER_OPTIONS } from '../constants';
+
+const createState = () => ({
+  discussions: [],
+  discussionSortOrder: ASC,
+  persistSortOrder: true,
+  convertedDisscussionIds: [],
+  targetNoteHash: null,
+  lastFetchedAt: null,
+  currentDiscussionId: null,
+  batchSuggestionsInfo: [],
+  currentlyFetchingDiscussions: false,
+  doneFetchingBatchDiscussions: false,
+  /**
+   * selectedCommentPosition & selectedCommentPositionHover structures are the same as `position.line_range`:
+   * {
+   *  start: { line_code: string, new_line: number, old_line:number, type: string },
+   *  end: { line_code: string, new_line: number, old_line:number, type: string },
+   * }
+   */
+  selectedCommentPosition: null,
+  selectedCommentPositionHover: null,
+
+  // View layer
+  isToggleStateButtonLoading: false,
+  isNotesFetched: false,
+  isLoading: true,
+  isLoadingDescriptionVersion: false,
+  isPromoteCommentToTimelineEventInProgress: false,
+
+  // holds endpoints and permissions provided through haml
+  notesData: {
+    markdownDocsPath: '',
+  },
+  userData: {},
+  noteableData: {
+    discussion_locked: false,
+    confidential: false, // TODO: Move data like this to Issue Store, should not be apart of notes.
+    current_user: {},
+    preview_note_path: 'path/to/preview',
+  },
+  isResolvingDiscussion: false,
+  commentsDisabled: false,
+  resolvableDiscussionsCount: 0,
+  unresolvedDiscussionsCount: 0,
+  descriptionVersions: {},
+  isTimelineEnabled: false,
+  isFetching: false,
+  isPollingInitialized: false,
+  mergeRequestFilters: MR_FILTER_OPTIONS.map((f) => f.value),
+});
+
+export default createState;
diff --git a/ee/spec/frontend/diffs/components/app_spec.js b/ee/spec/frontend/diffs/components/app_spec.js
index c179446aa283b4f7c25f5979bb2a17a63ecb9be5..b0c39be577531ef7e94768331e0c1e9bfb11db79 100644
--- a/ee/spec/frontend/diffs/components/app_spec.js
+++ b/ee/spec/frontend/diffs/components/app_spec.js
@@ -1,34 +1,35 @@
 import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
 
 import Vue from 'vue';
-import Vuex from 'vuex';
-import { stubPerformanceWebAPI } from 'helpers/performance';
-import createDiffsStore from 'jest/diffs/create_diffs_store';
 import { TEST_HOST } from 'spec/test_constants';
 import App from '~/diffs/components/app.vue';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import store from '~/mr_notes/stores';
 
 const TEST_ENDPOINT = `${TEST_HOST}/diff/endpoint`;
 
-Vue.use(Vuex);
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 Vue.config.ignoredElements = ['copy-code'];
 
 describe('diffs/components/app', () => {
-  let store;
-  let wrapper;
-  let mock;
+  const createComponent = (props = {}) => {
+    store.reset();
+
+    store.getters.isNotesFetched = false;
+    store.getters.getNoteableData = {
+      current_user: {
+        can_create_note: true,
+      },
+    };
+    store.getters['diffs/flatBlobsList'] = [];
+    store.getters['diffs/isBatchLoading'] = false;
+    store.getters['diffs/isBatchLoadingError'] = false;
+    store.getters['diffs/whichCollapsedTypes'] = { any: false };
 
-  function createComponent(props = {}, extendStore = () => {}) {
-    store = createDiffsStore();
     store.state.diffs.isLoading = false;
     store.state.diffs.isTreeLoaded = true;
 
-    extendStore(store);
-
-    wrapper = shallowMount(App, {
+    return shallowMount(App, {
       propsData: {
         endpoint: TEST_ENDPOINT,
         endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
@@ -44,20 +45,15 @@ describe('diffs/components/app', () => {
         fileByFileUserPreference: false,
         ...props,
       },
-      store,
+      mocks: {
+        $store: store,
+      },
     });
-  }
-
-  beforeEach(() => {
-    stubPerformanceWebAPI();
-
-    mock = new MockAdapter(axios);
-    mock.onGet(TEST_ENDPOINT).reply(HTTP_STATUS_OK, {});
-  });
+  };
 
   describe('EE codequality diff', () => {
     it('fetches code quality data when endpoint is provided', () => {
-      createComponent();
+      const wrapper = createComponent();
       jest.spyOn(wrapper.vm, 'fetchCodequality');
       wrapper.vm.fetchData(false);
 
@@ -65,7 +61,7 @@ describe('diffs/components/app', () => {
     });
 
     it('does not fetch code quality data when endpoint is blank', () => {
-      createComponent({ endpointCodequality: '' });
+      const wrapper = createComponent({ endpointCodequality: '' });
       jest.spyOn(wrapper.vm, 'fetchCodequality');
       wrapper.vm.fetchData(false);
 
diff --git a/ee/spec/frontend/diffs/components/code_quality_gutter_icon_spec.js b/ee/spec/frontend/diffs/components/code_quality_gutter_icon_spec.js
index 3cd550fda3129334ed60b9aafc055f41acad0336..db2f6737a44e9c0a5802949eb9f0251bdb186898 100644
--- a/ee/spec/frontend/diffs/components/code_quality_gutter_icon_spec.js
+++ b/ee/spec/frontend/diffs/components/code_quality_gutter_icon_spec.js
@@ -1,9 +1,8 @@
 import { GlIcon, GlTooltip } from '@gitlab/ui';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
+import { nextTick } from 'vue';
 import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import CodeQualityGutterIcon from 'ee/diffs/components/code_quality_gutter_icon.vue';
-import createDiffsStore from 'jest/diffs/create_diffs_store';
+import store from '~/mr_notes/stores';
 import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
 import {
   fiveFindings,
@@ -11,23 +10,24 @@ import {
   singularFinding,
 } from '../../../../../spec/frontend/diffs/mock_data/diff_code_quality';
 
-Vue.use(Vuex);
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 let wrapper;
 const findIcon = () => wrapper.findComponent(GlIcon);
 const findIcons = () => wrapper.findAllComponents(GlIcon);
 const findFirstIcon = () => wrapper.findComponent({ ref: 'firstCodeQualityIcon' });
 
-let store;
 let codequalityDiff;
 
 const createComponent = (props = {}) => {
-  store = createDiffsStore();
+  store.reset();
   store.state.diffs.codequalityDiff = codequalityDiff;
 
   const payload = {
     propsData: props,
-    store,
+    mocks: {
+      $store: store,
+    },
   };
 
   wrapper = shallowMountExtended(CodeQualityGutterIcon, payload);
diff --git a/package.json b/package.json
index 30dafb88268cd082dc7343618fcb36b9d473cb18..fed66bb53c536e9d7a848a3feff720f2fa060952 100644
--- a/package.json
+++ b/package.json
@@ -279,6 +279,7 @@
     "timezone-mock": "^1.0.8",
     "vue-loader-vue3": "npm:vue-loader@17",
     "vue-test-utils-compat": "0.0.13",
+    "vuex-mock-store": "^0.1.0",
     "webpack-dev-server": "4.15.0",
     "xhr-mock": "^2.5.1",
     "yarn-check-webpack-plugin": "^1.2.0",
diff --git a/spec/frontend/__helpers__/mocks/mr_notes/stores/index.js b/spec/frontend/__helpers__/mocks/mr_notes/stores/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..a983edbbb72633d2e96472e75a370b23ae4354db
--- /dev/null
+++ b/spec/frontend/__helpers__/mocks/mr_notes/stores/index.js
@@ -0,0 +1,15 @@
+import { Store } from 'vuex-mock-store';
+import createDiffState from 'ee_else_ce/diffs/store/modules/diff_state';
+import createNotesState from '~/notes/stores/state';
+
+const store = new Store({
+  state: {
+    diffs: createDiffState(),
+    notes: createNotesState(),
+  },
+  spy: {
+    create: (handler) => jest.fn(handler).mockImplementation(() => Promise.resolve()),
+  },
+});
+
+export default store;
diff --git a/spec/frontend/batch_comments/components/preview_item_spec.js b/spec/frontend/batch_comments/components/preview_item_spec.js
index a19a72af813ad4c18035ed839ee8cf2c48054941..191586e44cc6152ad7f25b33d2d9e425213c319c 100644
--- a/spec/frontend/batch_comments/components/preview_item_spec.js
+++ b/spec/frontend/batch_comments/components/preview_item_spec.js
@@ -1,29 +1,33 @@
 import { mount } from '@vue/test-utils';
 import PreviewItem from '~/batch_comments/components/preview_item.vue';
-import { createStore } from '~/batch_comments/stores';
-import diffsModule from '~/diffs/store/modules';
-import notesModule from '~/notes/stores/modules';
+import store from '~/mr_notes/stores';
 import { createDraft } from '../mock_data';
 
 jest.mock('~/behaviors/markdown/render_gfm');
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 describe('Batch comments draft preview item component', () => {
   let wrapper;
   let draft;
 
-  function createComponent(isLast = false, extra = {}, extendStore = () => {}) {
-    const store = createStore();
-    store.registerModule('diffs', diffsModule());
-    store.registerModule('notes', notesModule());
+  beforeEach(() => {
+    store.reset();
 
-    extendStore(store);
+    store.getters.getDiscussion = jest.fn(() => null);
+  });
 
+  function createComponent(isLast = false, extra = {}) {
     draft = {
       ...createDraft(),
       ...extra,
     };
 
-    wrapper = mount(PreviewItem, { store, propsData: { draft, isLast } });
+    wrapper = mount(PreviewItem, {
+      mocks: {
+        $store: store,
+      },
+      propsData: { draft, isLast },
+    });
   }
 
   it('renders text content', () => {
@@ -87,18 +91,19 @@ describe('Batch comments draft preview item component', () => {
 
   describe('for thread', () => {
     beforeEach(() => {
-      createComponent(false, { discussion_id: '1', resolve_discussion: true }, (store) => {
-        store.state.notes.discussions.push({
-          id: '1',
-          notes: [
-            {
-              author: {
-                name: "Author 'Nick' Name",
-              },
+      store.getters.getDiscussion.mockReturnValue({
+        id: '1',
+        notes: [
+          {
+            author: {
+              name: "Author 'Nick' Name",
             },
-          ],
-        });
+          },
+        ],
       });
+      store.getters.isDiscussionResolved = jest.fn().mockReturnValue(false);
+
+      createComponent(false, { discussion_id: '1', resolve_discussion: true });
     });
 
     it('renders title', () => {
@@ -114,9 +119,7 @@ describe('Batch comments draft preview item component', () => {
 
   describe('for new comment', () => {
     it('renders title', () => {
-      createComponent(false, {}, (store) => {
-        store.state.notes.discussions.push({});
-      });
+      createComponent();
 
       expect(wrapper.find('.review-preview-item-header-text').text()).toContain('Your new comment');
     });
diff --git a/spec/frontend/diffs/components/compare_versions_spec.js b/spec/frontend/diffs/components/compare_versions_spec.js
index 47a266c2e365dee74ba436f14bf8d865c3b55033..cbbfd88260bb3b3d14e526fcfbf56c6f42619a02 100644
--- a/spec/frontend/diffs/components/compare_versions_spec.js
+++ b/spec/frontend/diffs/components/compare_versions_spec.js
@@ -1,15 +1,14 @@
 import { mount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import Vuex from 'vuex';
+import { nextTick } from 'vue';
 import getDiffWithCommit from 'test_fixtures/merge_request_diffs/with_commit.json';
 import setWindowLocation from 'helpers/set_window_location_helper';
 import { TEST_HOST } from 'helpers/test_constants';
 import { trimText } from 'helpers/text_helper';
 import CompareVersionsComponent from '~/diffs/components/compare_versions.vue';
-import { createStore } from '~/mr_notes/stores';
+import store from '~/mr_notes/stores';
 import diffsMockData from '../mock_data/merge_request_diffs';
 
-Vue.use(Vuex);
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 const NEXT_COMMIT_URL = `${TEST_HOST}/?commit_id=next`;
 const PREV_COMMIT_URL = `${TEST_HOST}/?commit_id=prev`;
@@ -20,8 +19,6 @@ beforeEach(() => {
 
 describe('CompareVersions', () => {
   let wrapper;
-  let store;
-  let dispatchMock;
   const targetBranchName = 'tmp-wine-dev';
   const { commit } = getDiffWithCommit;
 
@@ -30,10 +27,10 @@ describe('CompareVersions', () => {
       store.state.diffs.commit = { ...store.state.diffs.commit, ...commitArgs };
     }
 
-    dispatchMock = jest.spyOn(store, 'dispatch');
-
     wrapper = mount(CompareVersionsComponent, {
-      store,
+      mocks: {
+        $store: store,
+      },
       propsData: {
         mergeRequestDiffs: diffsMockData,
         diffFilesCountText: '1',
@@ -50,8 +47,25 @@ describe('CompareVersions', () => {
     getCommitNavButtonsElement().find('.btn-group > *:first-child');
 
   beforeEach(() => {
-    store = createStore();
+    store.reset();
+
     const mergeRequestDiff = diffsMockData[0];
+    const version = {
+      ...mergeRequestDiff,
+      href: `${TEST_HOST}/latest/version`,
+      versionName: 'latest version',
+    };
+    store.getters['diffs/diffCompareDropdownSourceVersions'] = [version];
+    store.getters['diffs/diffCompareDropdownTargetVersions'] = [
+      {
+        ...version,
+        selected: true,
+        versionName: targetBranchName,
+      },
+    ];
+    store.getters['diffs/whichCollapsedTypes'] = { any: false };
+    store.getters['diffs/isInlineView'] = false;
+    store.getters['diffs/isParallelView'] = false;
 
     store.state.diffs.addedLines = 10;
     store.state.diffs.removedLines = 20;
@@ -104,7 +118,6 @@ describe('CompareVersions', () => {
 
     it('should not render Tree List toggle button when there are no changes', () => {
       createWrapper();
-
       const treeListBtn = wrapper.find('.js-toggle-tree-list');
 
       expect(treeListBtn.exists()).toBe(false);
@@ -118,7 +131,10 @@ describe('CompareVersions', () => {
       const viewTypeBtn = wrapper.find('#inline-diff-btn');
       viewTypeBtn.trigger('click');
 
-      expect(window.location.toString()).toContain('?view=inline');
+      expect(store.dispatch).toHaveBeenCalledWith(
+        'diffs/setInlineDiffViewType',
+        expect.any(MouseEvent),
+      );
     });
   });
 
@@ -128,13 +144,16 @@ describe('CompareVersions', () => {
       const viewTypeBtn = wrapper.find('#parallel-diff-btn');
       viewTypeBtn.trigger('click');
 
-      expect(window.location.toString()).toContain('?view=parallel');
+      expect(store.dispatch).toHaveBeenCalledWith(
+        'diffs/setParallelDiffViewType',
+        expect.any(MouseEvent),
+      );
     });
   });
 
   describe('commit', () => {
     beforeEach(() => {
-      store.state.diffs.commit = getDiffWithCommit.commit;
+      store.state.diffs.commit = commit;
       createWrapper();
     });
 
@@ -218,7 +237,7 @@ describe('CompareVersions', () => {
 
         link.trigger('click');
         await nextTick();
-        expect(dispatchMock).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
+        expect(store.dispatch).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
           direction: 'previous',
         });
       });
@@ -248,7 +267,7 @@ describe('CompareVersions', () => {
 
         link.trigger('click');
         await nextTick();
-        expect(dispatchMock).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
+        expect(store.dispatch).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
           direction: 'next',
         });
       });
diff --git a/spec/frontend/diffs/components/diff_line_note_form_spec.js b/spec/frontend/diffs/components/diff_line_note_form_spec.js
index eb895bd90573ac08ab5498793a40bc8d016eeb9a..e42b98e4d68bd5213e89ec75d714a1f18c52549e 100644
--- a/spec/frontend/diffs/components/diff_line_note_form_spec.js
+++ b/spec/frontend/diffs/components/diff_line_note_form_spec.js
@@ -1,8 +1,7 @@
 import { shallowMount } from '@vue/test-utils';
 import { nextTick } from 'vue';
-import Vuex from 'vuex';
 import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue';
-import { createModules } from '~/mr_notes/stores';
+import store from '~/mr_notes/stores';
 import NoteForm from '~/notes/components/note_form.vue';
 import MultilineCommentForm from '~/notes/components/multiline_comment_form.vue';
 import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
@@ -10,51 +9,25 @@ import { noteableDataMock } from 'jest/notes/mock_data';
 import { getDiffFileMock } from '../mock_data/diff_file';
 
 jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 describe('DiffLineNoteForm', () => {
   let wrapper;
   let diffFile;
   let diffLines;
-  let actions;
-  let store;
 
-  const getSelectedLine = () => {
-    const lineCode = diffLines[1].line_code;
-    return diffFile.highlighted_diff_lines.find((l) => l.line_code === lineCode);
-  };
-
-  const createStore = (state) => {
-    const modules = createModules();
-    modules.diffs.actions = {
-      ...modules.diffs.actions,
-      saveDiffDiscussion: jest.fn(() => Promise.resolve()),
-    };
-    modules.diffs.getters = {
-      ...modules.diffs.getters,
-      diffCompareDropdownTargetVersions: jest.fn(),
-      diffCompareDropdownSourceVersions: jest.fn(),
-      selectedSourceIndex: jest.fn(),
-    };
-    modules.notes.getters = {
-      ...modules.notes.getters,
-      noteableType: jest.fn(),
-    };
-    actions = modules.diffs.actions;
+  beforeEach(() => {
+    diffFile = getDiffFileMock();
+    diffLines = diffFile.highlighted_diff_lines;
 
-    store = new Vuex.Store({ modules });
-    store.state.notes.userData.id = 1;
     store.state.notes.noteableData = noteableDataMock;
 
-    store.replaceState({ ...store.state, ...state });
-  };
+    store.getters.isLoggedIn = jest.fn().mockReturnValue(true);
+    store.getters['diffs/getDiffFileByHash'] = jest.fn().mockReturnValue(diffFile);
+  });
 
-  const createComponent = ({ props, state } = {}) => {
+  const createComponent = ({ props } = {}) => {
     wrapper?.destroy();
-    diffFile = getDiffFileMock();
-    diffLines = diffFile.highlighted_diff_lines;
-
-    createStore(state);
-    store.state.diffs.diffFiles = [diffFile];
 
     const propsData = {
       diffFileHash: diffFile.file_hash,
@@ -66,7 +39,9 @@ describe('DiffLineNoteForm', () => {
     };
 
     wrapper = shallowMount(DiffLineNoteForm, {
-      store,
+      mocks: {
+        $store: store,
+      },
       propsData,
     });
   };
@@ -129,7 +104,10 @@ describe('DiffLineNoteForm', () => {
         expect(confirmAction).toHaveBeenCalled();
         await nextTick();
 
-        expect(getSelectedLine().hasForm).toBe(false);
+        expect(store.dispatch).toHaveBeenCalledWith('diffs/cancelCommentForm', {
+          lineCode: diffLines[1].line_code,
+          fileHash: diffFile.file_hash,
+        });
       });
     });
 
@@ -157,6 +135,10 @@ describe('DiffLineNoteForm', () => {
   });
 
   describe('saving note', () => {
+    beforeEach(() => {
+      store.getters.noteableType = 'merge-request';
+    });
+
     it('should save original line', async () => {
       const lineRange = {
         start: {
@@ -172,20 +154,65 @@ describe('DiffLineNoteForm', () => {
           old_line: null,
         },
       };
-      await findNoteForm().vm.$emit('handleFormUpdate', 'note body');
-      expect(actions.saveDiffDiscussion.mock.calls[0][1].formData).toMatchObject({
-        lineRange,
+
+      const noteBody = 'note body';
+      await findNoteForm().vm.$emit('handleFormUpdate', noteBody);
+
+      expect(store.dispatch).toHaveBeenCalledWith('diffs/saveDiffDiscussion', {
+        note: noteBody,
+        formData: {
+          noteableData: noteableDataMock,
+          noteableType: store.getters.noteableType,
+          noteTargetLine: diffLines[1],
+          diffViewType: store.state.diffs.diffViewType,
+          diffFile,
+          linePosition: '',
+          lineRange,
+        },
+      });
+      expect(store.dispatch).toHaveBeenCalledWith('diffs/cancelCommentForm', {
+        lineCode: diffLines[1].line_code,
+        fileHash: diffFile.file_hash,
       });
     });
 
     it('should save selected line from the store', async () => {
       const lineCode = 'test';
       store.state.notes.selectedCommentPosition = { start: { line_code: lineCode } };
-      createComponent({ state: store.state });
-      await findNoteForm().vm.$emit('handleFormUpdate', 'note body');
-      expect(actions.saveDiffDiscussion.mock.calls[0][1].formData.lineRange.start.line_code).toBe(
-        lineCode,
-      );
+      createComponent();
+      const noteBody = 'note body';
+
+      await findNoteForm().vm.$emit('handleFormUpdate', noteBody);
+
+      expect(store.dispatch).toHaveBeenCalledWith('diffs/saveDiffDiscussion', {
+        note: noteBody,
+        formData: {
+          noteableData: noteableDataMock,
+          noteableType: store.getters.noteableType,
+          noteTargetLine: diffLines[1],
+          diffViewType: store.state.diffs.diffViewType,
+          diffFile,
+          linePosition: '',
+          lineRange: {
+            start: {
+              line_code: lineCode,
+              new_line: undefined,
+              old_line: undefined,
+              type: undefined,
+            },
+            end: {
+              line_code: diffLines[1].line_code,
+              new_line: diffLines[1].new_line,
+              old_line: diffLines[1].old_line,
+              type: diffLines[1].type,
+            },
+          },
+        },
+      });
+      expect(store.dispatch).toHaveBeenCalledWith('diffs/cancelCommentForm', {
+        lineCode: diffLines[1].line_code,
+        fileHash: diffFile.file_hash,
+      });
     });
   });
 });
diff --git a/spec/frontend/diffs/components/no_changes_spec.js b/spec/frontend/diffs/components/no_changes_spec.js
index e637b1dd43db08e92f032007e636c791215fbade..fd89d52a59e0e8c368268db3014faed03dbfaf5a 100644
--- a/spec/frontend/diffs/components/no_changes_spec.js
+++ b/spec/frontend/diffs/components/no_changes_spec.js
@@ -1,55 +1,53 @@
 import { GlButton } from '@gitlab/ui';
 import { shallowMount, mount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
 import NoChanges from '~/diffs/components/no_changes.vue';
-import { createStore } from '~/mr_notes/stores';
+import store from '~/mr_notes/stores';
 import diffsMockData from '../mock_data/merge_request_diffs';
 
-Vue.use(Vuex);
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 const TEST_TARGET_BRANCH = 'foo';
 const TEST_SOURCE_BRANCH = 'dev/update';
+const latestVersionNumber = Math.max(...diffsMockData.map((version) => version.version_index));
 
 describe('Diff no changes empty state', () => {
-  let wrapper;
-  let store;
-
-  function createComponent(mountFn = shallowMount) {
-    wrapper = mountFn(NoChanges, {
-      store,
+  const createComponent = (mountFn = shallowMount) =>
+    mountFn(NoChanges, {
+      mocks: {
+        $store: store,
+      },
       propsData: {
         changesEmptyStateIllustration: '',
       },
     });
-  }
 
   beforeEach(() => {
-    store = createStore();
-    store.state.diffs.mergeRequestDiff = {};
-    store.state.notes.noteableData = {
+    store.reset();
+
+    store.getters.getNoteableData = {
       target_branch: TEST_TARGET_BRANCH,
       source_branch: TEST_SOURCE_BRANCH,
     };
-    store.state.diffs.mergeRequestDiffs = diffsMockData;
+    store.getters['diffs/diffCompareDropdownSourceVersions'] = [];
+    store.getters['diffs/diffCompareDropdownTargetVersions'] = [];
   });
 
-  const findMessage = () => wrapper.find('[data-testid="no-changes-message"]');
+  const findMessage = (wrapper) => wrapper.find('[data-testid="no-changes-message"]');
 
   it('prevents XSS', () => {
-    store.state.notes.noteableData = {
+    store.getters.getNoteableData = {
       source_branch: '<script>alert("test");</script>',
       target_branch: '<script>alert("test");</script>',
     };
 
-    createComponent();
+    const wrapper = createComponent();
 
     expect(wrapper.find('script').exists()).toBe(false);
   });
 
   describe('Renders', () => {
     it('Show create commit button', () => {
-      createComponent();
+      const wrapper = createComponent();
 
       expect(wrapper.findComponent(GlButton).exists()).toBe(true);
     });
@@ -64,15 +62,28 @@ describe('Diff no changes empty state', () => {
       'renders text "$expectedText" (sourceIndex=$sourceIndex and targetIndex=$targetIndex)',
       ({ expectedText, targetIndex, sourceIndex }) => {
         if (targetIndex !== null) {
-          store.state.diffs.startVersion = { version_index: targetIndex };
+          store.getters['diffs/diffCompareDropdownTargetVersions'] = [
+            {
+              selected: true,
+              version_index: targetIndex,
+              versionName: `version ${targetIndex}`,
+            },
+          ];
         }
         if (sourceIndex !== null) {
-          store.state.diffs.mergeRequestDiff.version_index = sourceIndex;
+          store.getters['diffs/diffCompareDropdownSourceVersions'] = [
+            {
+              isLatestVersion: sourceIndex === latestVersionNumber,
+              selected: true,
+              version_index: targetIndex,
+              versionName: `version ${sourceIndex}`,
+            },
+          ];
         }
 
-        createComponent(mount);
+        const wrapper = createComponent(mount);
 
-        expect(findMessage().text()).toBe(expectedText);
+        expect(findMessage(wrapper).text()).toBe(expectedText);
       },
     );
   });
diff --git a/spec/frontend/diffs/components/settings_dropdown_spec.js b/spec/frontend/diffs/components/settings_dropdown_spec.js
index 3d2bbe437467e54d9b5d131d9ae0ae208bd7b45a..cbd2ae3e525eb45e0a91f5d7f1e7324f6e133b49 100644
--- a/spec/frontend/diffs/components/settings_dropdown_spec.js
+++ b/spec/frontend/diffs/components/settings_dropdown_spec.js
@@ -5,44 +5,34 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
 import SettingsDropdown from '~/diffs/components/settings_dropdown.vue';
 import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
 import eventHub from '~/diffs/event_hub';
+import store from '~/mr_notes/stores';
 
-import createDiffsStore from '../create_diffs_store';
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 describe('Diff settings dropdown component', () => {
-  let wrapper;
-  let store;
-
-  function createComponent(extendStore = () => {}) {
-    store = createDiffsStore();
-
-    extendStore(store);
-
-    wrapper = extendedWrapper(
+  const createComponent = () =>
+    extendedWrapper(
       mount(SettingsDropdown, {
-        store,
+        mocks: {
+          $store: store,
+        },
       }),
     );
-  }
 
   function getFileByFileCheckbox(vueWrapper) {
     return vueWrapper.findByTestId('file-by-file');
   }
 
-  function setup({ storeUpdater } = {}) {
-    createComponent(storeUpdater);
-    jest.spyOn(store, 'dispatch').mockImplementation(() => {});
-  }
-
   beforeEach(() => {
-    setup();
-  });
+    store.reset();
 
-  afterEach(() => {
-    store.dispatch.mockRestore();
+    store.getters['diffs/isInlineView'] = false;
+    store.getters['diffs/isParallelView'] = false;
   });
 
   describe('tree view buttons', () => {
     it('list view button dispatches setRenderTreeList with false', () => {
+      const wrapper = createComponent();
       wrapper.find('.js-list-view').trigger('click');
 
       expect(store.dispatch).toHaveBeenCalledWith('diffs/setRenderTreeList', {
@@ -51,6 +41,7 @@ describe('Diff settings dropdown component', () => {
     });
 
     it('tree view button dispatches setRenderTreeList with true', () => {
+      const wrapper = createComponent();
       wrapper.find('.js-tree-view').trigger('click');
 
       expect(store.dispatch).toHaveBeenCalledWith('diffs/setRenderTreeList', {
@@ -59,19 +50,18 @@ describe('Diff settings dropdown component', () => {
     });
 
     it('sets list button as selected when renderTreeList is false', () => {
-      setup({
-        storeUpdater: (origStore) =>
-          Object.assign(origStore.state.diffs, { renderTreeList: false }),
-      });
+      store.state.diffs = { renderTreeList: false };
+
+      const wrapper = createComponent();
 
       expect(wrapper.find('.js-list-view').classes('selected')).toBe(true);
       expect(wrapper.find('.js-tree-view').classes('selected')).toBe(false);
     });
 
     it('sets tree button as selected when renderTreeList is true', () => {
-      setup({
-        storeUpdater: (origStore) => Object.assign(origStore.state.diffs, { renderTreeList: true }),
-      });
+      store.state.diffs = { renderTreeList: true };
+
+      const wrapper = createComponent();
 
       expect(wrapper.find('.js-list-view').classes('selected')).toBe(false);
       expect(wrapper.find('.js-tree-view').classes('selected')).toBe(true);
@@ -80,32 +70,36 @@ describe('Diff settings dropdown component', () => {
 
   describe('compare changes', () => {
     it('sets inline button as selected', () => {
-      setup({
-        storeUpdater: (origStore) =>
-          Object.assign(origStore.state.diffs, { diffViewType: INLINE_DIFF_VIEW_TYPE }),
-      });
+      store.state.diffs = { diffViewType: INLINE_DIFF_VIEW_TYPE };
+      store.getters['diffs/isInlineView'] = true;
+
+      const wrapper = createComponent();
 
       expect(wrapper.find('.js-inline-diff-button').classes('selected')).toBe(true);
       expect(wrapper.find('.js-parallel-diff-button').classes('selected')).toBe(false);
     });
 
     it('sets parallel button as selected', () => {
-      setup({
-        storeUpdater: (origStore) =>
-          Object.assign(origStore.state.diffs, { diffViewType: PARALLEL_DIFF_VIEW_TYPE }),
-      });
+      store.state.diffs = { diffViewType: PARALLEL_DIFF_VIEW_TYPE };
+      store.getters['diffs/isParallelView'] = true;
+
+      const wrapper = createComponent();
 
       expect(wrapper.find('.js-inline-diff-button').classes('selected')).toBe(false);
       expect(wrapper.find('.js-parallel-diff-button').classes('selected')).toBe(true);
     });
 
     it('calls setInlineDiffViewType when clicking inline button', () => {
+      const wrapper = createComponent();
+
       wrapper.find('.js-inline-diff-button').trigger('click');
 
       expect(store.dispatch).toHaveBeenCalledWith('diffs/setInlineDiffViewType', expect.anything());
     });
 
     it('calls setParallelDiffViewType when clicking parallel button', () => {
+      const wrapper = createComponent();
+
       wrapper.find('.js-parallel-diff-button').trigger('click');
 
       expect(store.dispatch).toHaveBeenCalledWith(
@@ -117,23 +111,23 @@ describe('Diff settings dropdown component', () => {
 
   describe('whitespace toggle', () => {
     it('does not set as checked when showWhitespace is false', () => {
-      setup({
-        storeUpdater: (origStore) =>
-          Object.assign(origStore.state.diffs, { showWhitespace: false }),
-      });
+      store.state.diffs = { showWhitespace: false };
+
+      const wrapper = createComponent();
 
       expect(wrapper.findByTestId('show-whitespace').element.checked).toBe(false);
     });
 
     it('sets as checked when showWhitespace is true', () => {
-      setup({
-        storeUpdater: (origStore) => Object.assign(origStore.state.diffs, { showWhitespace: true }),
-      });
+      store.state.diffs = { showWhitespace: true };
+
+      const wrapper = createComponent();
 
       expect(wrapper.findByTestId('show-whitespace').element.checked).toBe(true);
     });
 
     it('calls setShowWhitespace on change', async () => {
+      const wrapper = createComponent();
       const checkbox = wrapper.findByTestId('show-whitespace');
       const { checked } = checkbox.element;
 
@@ -157,10 +151,9 @@ describe('Diff settings dropdown component', () => {
     `(
       'sets the checkbox to { checked: $checked } if the fileByFile setting is $fileByFile',
       ({ fileByFile, checked }) => {
-        setup({
-          storeUpdater: (origStore) =>
-            Object.assign(origStore.state.diffs, { viewDiffsFileByFile: fileByFile }),
-        });
+        store.state.diffs = { viewDiffsFileByFile: fileByFile };
+
+        const wrapper = createComponent();
 
         expect(getFileByFileCheckbox(wrapper).element.checked).toBe(checked);
       },
@@ -173,11 +166,9 @@ describe('Diff settings dropdown component', () => {
     `(
       'when the file by file setting starts as $start, toggling the checkbox should call setFileByFile with $setting',
       async ({ start, setting }) => {
-        setup({
-          storeUpdater: (origStore) =>
-            Object.assign(origStore.state.diffs, { viewDiffsFileByFile: start }),
-        });
+        store.state.diffs = { viewDiffsFileByFile: start };
 
+        const wrapper = createComponent();
         await getFileByFileCheckbox(wrapper).setChecked(setting);
 
         expect(store.dispatch).toHaveBeenCalledWith('diffs/setFileByFile', {
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index f883aea764fd203e6d45b6fe17f5b5e01656fdf0..f084458b5c77681d26b3be3c60698f85f3061e82 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -707,6 +707,7 @@ describe('DiffsStoreActions', () => {
         [{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }],
         [],
       );
+      expect(window.location.toString()).toContain('?view=inline');
       expect(Cookies.get('diff_view')).toEqual(INLINE_DIFF_VIEW_TYPE);
     });
   });
@@ -720,6 +721,7 @@ describe('DiffsStoreActions', () => {
         [{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }],
         [],
       );
+      expect(window.location.toString()).toContain('?view=parallel');
       expect(Cookies.get(DIFF_VIEW_COOKIE_NAME)).toEqual(PARALLEL_DIFF_VIEW_TYPE);
     });
   });
diff --git a/spec/frontend/issuable/components/issuable_header_warnings_spec.js b/spec/frontend/issuable/components/issuable_header_warnings_spec.js
index ff772040d22d384d0d16046ffaeb6f35ad8b84a4..cbe2810a44395146bda2108faad60ead5154188e 100644
--- a/spec/frontend/issuable/components/issuable_header_warnings_spec.js
+++ b/spec/frontend/issuable/components/issuable_header_warnings_spec.js
@@ -1,15 +1,13 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
 import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
 import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createStore as createMrStore } from '~/mr_notes/stores';
+import mrStore from '~/mr_notes/stores';
 import createIssueStore from '~/notes/stores';
 import IssuableHeaderWarnings from '~/issuable/components/issuable_header_warnings.vue';
 
 const ISSUABLE_TYPE_ISSUE = 'issue';
 const ISSUABLE_TYPE_MR = 'merge_request';
 
-Vue.use(Vuex);
+jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores'));
 
 describe('IssuableHeaderWarnings', () => {
   let wrapper;
@@ -22,7 +20,9 @@ describe('IssuableHeaderWarnings', () => {
 
   const createComponent = ({ store, provide }) => {
     wrapper = shallowMountExtended(IssuableHeaderWarnings, {
-      store,
+      mocks: {
+        $store: store,
+      },
       provide,
       directives: {
         GlTooltip: createMockDirective('gl-tooltip'),
@@ -47,9 +47,14 @@ describe('IssuableHeaderWarnings', () => {
     `(
       `when locked=$lockStatus, confidential=$confidentialStatus, and hidden=$hiddenStatus`,
       ({ lockStatus, confidentialStatus, hiddenStatus }) => {
-        const store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : createMrStore();
+        const store = issuableType === ISSUABLE_TYPE_ISSUE ? createIssueStore() : mrStore;
 
         beforeEach(() => {
+          // TODO: simplify to single assignment after issue store is mock
+          if (store === mrStore) {
+            store.getters.getNoteableData = {};
+          }
+
           store.getters.getNoteableData.confidential = confidentialStatus;
           store.getters.getNoteableData.discussion_locked = lockStatus;
           store.getters.getNoteableData.targetType = issuableType;
diff --git a/yarn.lock b/yarn.lock
index 9c9a98d41842fc67a17ec1d43403bc5aff17a7a2..645a7f2b97f25c588bea77cbf99ecb2552bab6d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12826,6 +12826,13 @@ vuedraggable@^2.23.0:
   dependencies:
     sortablejs "^1.9.0"
 
+vuex-mock-store@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/vuex-mock-store/-/vuex-mock-store-0.1.0.tgz#eddd85d32184e93926b3693866ab43efdba95c9d"
+  integrity sha512-NWbZcw91sxDiCsFt4v6jSF6lr7GUBaVmdLvkYOIXIKtfbEUIN3OO+LuByaAzjmrskr5Xg7QQLueteQZoFLMBoA==
+  dependencies:
+    lodash.clonedeep "^4.5.0"
+
 "vuex-vue3@npm:vuex@4.0.0":
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.0.tgz#ac877aa76a9c45368c979471e461b520d38e6cf5"