summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-24 15:15:38 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-24 15:15:38 +0000
commitefbf661c4224d481c57d0346e26983a805e5ec93 (patch)
tree4736f287350884cb49d84a09c52c8c2e1b851080 /spec/frontend
parent4720346c2e10e1ff62a20b39dfc9866eb88858e6 (diff)
downloadgitlab-ce-efbf661c4224d481c57d0346e26983a805e5ec93.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/__mocks__/mousetrap/index.js6
-rw-r--r--spec/frontend/admin/broadcast_messages/components/message_form_spec.js12
-rw-r--r--spec/frontend/admin/broadcast_messages/components/messages_table_spec.js15
-rw-r--r--spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js26
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_compatibility_alert_spec.js32
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js66
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_token_spec.js22
-rw-r--r--spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js24
-rw-r--r--spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js21
-rw-r--r--spec/frontend/design_management/components/toolbar/design_navigation_spec.js3
-rw-r--r--spec/frontend/diffs/components/app_spec.js2
-rw-r--r--spec/frontend/jobs/components/job/stages_dropdown_spec.js2
-rw-r--r--spec/frontend/lib/mousetrap_spec.js113
-rw-r--r--spec/frontend/notes/components/discussion_navigator_spec.js3
-rw-r--r--spec/frontend/projects/settings/components/default_branch_selector_spec.js1
-rw-r--r--spec/frontend/ref/components/ref_selector_spec.js6
-rw-r--r--spec/frontend/shortcuts_spec.js79
-rw-r--r--spec/frontend/super_sidebar/components/super_sidebar_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/file_finder/index_spec.js2
-rw-r--r--spec/frontend/zen_mode_spec.js2
20 files changed, 305 insertions, 134 deletions
diff --git a/spec/frontend/__mocks__/mousetrap/index.js b/spec/frontend/__mocks__/mousetrap/index.js
deleted file mode 100644
index 63c92fa9a09..00000000000
--- a/spec/frontend/__mocks__/mousetrap/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* global Mousetrap */
-// `mousetrap` uses amd which webpack understands but Jest does not
-// Thankfully it also writes to a global export so we can es6-ify it
-import 'mousetrap';
-
-export default Mousetrap;
diff --git a/spec/frontend/admin/broadcast_messages/components/message_form_spec.js b/spec/frontend/admin/broadcast_messages/components/message_form_spec.js
index ba8b9dd1345..db87e8f09aa 100644
--- a/spec/frontend/admin/broadcast_messages/components/message_form_spec.js
+++ b/spec/frontend/admin/broadcast_messages/components/message_form_spec.js
@@ -36,10 +36,9 @@ describe('MessageForm', () => {
const findSubmitButton = () => wrapper.findComponent('[data-testid=submit-button]');
const findForm = () => wrapper.findComponent(GlForm);
- function createComponent({ broadcastMessage = {}, glFeatures = {} }) {
+ function createComponent({ broadcastMessage = {} } = {}) {
wrapper = mount(MessageForm, {
provide: {
- glFeatures,
targetAccessLevelOptions: MOCK_TARGET_ACCESS_LEVELS,
messagesPath,
previewPath: '_preview_path_',
@@ -100,15 +99,10 @@ describe('MessageForm', () => {
});
describe('target roles checkboxes', () => {
- it('renders when roleTargetedBroadcastMessages feature is enabled', () => {
- createComponent({ glFeatures: { roleTargetedBroadcastMessages: true } });
+ it('renders target roles', () => {
+ createComponent();
expect(findTargetRoles().exists()).toBe(true);
});
-
- it('does not render when roleTargetedBroadcastMessages feature is disabled', () => {
- createComponent({ glFeatures: { roleTargetedBroadcastMessages: false } });
- expect(findTargetRoles().exists()).toBe(false);
- });
});
describe('form submit button', () => {
diff --git a/spec/frontend/admin/broadcast_messages/components/messages_table_spec.js b/spec/frontend/admin/broadcast_messages/components/messages_table_spec.js
index 432bfefeb18..6d536b2d0e4 100644
--- a/spec/frontend/admin/broadcast_messages/components/messages_table_spec.js
+++ b/spec/frontend/admin/broadcast_messages/components/messages_table_spec.js
@@ -9,11 +9,8 @@ describe('MessagesTable', () => {
const findTargetRoles = () => wrapper.find('[data-testid="target-roles-th"]');
const findDeleteButton = (id) => wrapper.find(`[data-testid="delete-message-${id}"]`);
- function createComponent(props = {}, glFeatures = {}) {
+ function createComponent(props = {}) {
wrapper = mount(MessagesTable, {
- provide: {
- glFeatures,
- },
propsData: {
messages: MOCK_MESSAGES,
...props,
@@ -27,14 +24,10 @@ describe('MessagesTable', () => {
expect(findRows()).toHaveLength(MOCK_MESSAGES.length);
});
- it('renders the "Target Roles" column when roleTargetedBroadcastMessages is enabled', () => {
- createComponent({}, { roleTargetedBroadcastMessages: true });
- expect(findTargetRoles().exists()).toBe(true);
- });
-
- it('does not render the "Target Roles" column when roleTargetedBroadcastMessages is disabled', () => {
+ it('renders the "Target Roles" column', () => {
createComponent();
- expect(findTargetRoles().exists()).toBe(false);
+
+ expect(findTargetRoles().exists()).toBe(true);
});
it('emits a delete-message event when a delete button is clicked', () => {
diff --git a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
index 65336edd0d8..a454066b457 100644
--- a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
@@ -2,12 +2,10 @@ import { GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import AdminNewRunnerApp from '~/ci/runner/admin_new_runner/admin_new_runner_app.vue';
import { saveAlertToLocalStorage } from '~/ci/runner/local_storage_alert/save_alert_to_local_storage';
-import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue';
import {
PARAM_KEY_PLATFORM,
@@ -17,7 +15,7 @@ import {
} from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
import { redirectTo } from '~/lib/utils/url_utility';
-import { runnerCreateResult, mockRegistrationToken } from '../mock_data';
+import { runnerCreateResult } from '../mock_data';
jest.mock('~/ci/runner/local_storage_alert/save_alert_to_local_storage');
jest.mock('~/alert');
@@ -31,19 +29,11 @@ const mockCreatedRunner = runnerCreateResult.data.runnerCreate.runner;
describe('AdminNewRunnerApp', () => {
let wrapper;
- const findLegacyInstructionsLink = () => wrapper.findByTestId('legacy-instructions-link');
- const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
const findRunnerPlatformsRadioGroup = () => wrapper.findComponent(RunnerPlatformsRadioGroup);
const findRunnerCreateForm = () => wrapper.findComponent(RunnerCreateForm);
const createComponent = () => {
wrapper = shallowMountExtended(AdminNewRunnerApp, {
- propsData: {
- legacyRegistrationToken: mockRegistrationToken,
- },
- directives: {
- GlModal: createMockDirective('gl-modal'),
- },
stubs: {
GlSprintf,
},
@@ -54,20 +44,6 @@ describe('AdminNewRunnerApp', () => {
createComponent();
});
- describe('Shows legacy modal', () => {
- it('passes legacy registration to modal', () => {
- expect(findRunnerInstructionsModal().props('registrationToken')).toEqual(
- mockRegistrationToken,
- );
- });
-
- it('opens a modal with the legacy instructions', () => {
- const modalId = getBinding(findLegacyInstructionsLink().element, 'gl-modal').value;
-
- expect(findRunnerInstructionsModal().props('modalId')).toBe(modalId);
- });
- });
-
describe('Platform', () => {
it('shows the platforms radio group', () => {
expect(findRunnerPlatformsRadioGroup().props('value')).toBe(DEFAULT_PLATFORM);
diff --git a/spec/frontend/ci/runner/components/registration/registration_compatibility_alert_spec.js b/spec/frontend/ci/runner/components/registration/registration_compatibility_alert_spec.js
new file mode 100644
index 00000000000..ba8a1ac6744
--- /dev/null
+++ b/spec/frontend/ci/runner/components/registration/registration_compatibility_alert_spec.js
@@ -0,0 +1,32 @@
+import { GlAlert, GlLink } from '@gitlab/ui';
+import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
+import { CHANGELOG_URL } from '~/ci/runner/constants';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+
+describe('RegistrationCompatibilityAlert', () => {
+ let wrapper;
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLink = () => wrapper.findComponent(GlLink);
+
+ const createComponent = (mountFn = shallowMountExtended) => {
+ wrapper = mountFn(RegistrationCompatibilityAlert);
+ };
+
+ it('alert has warning appearance', () => {
+ createComponent();
+
+ expect(findAlert().props()).toMatchObject({
+ dismissible: false,
+ variant: 'warning',
+ title: expect.any(String),
+ });
+ });
+
+ it('shows alert content and link', () => {
+ createComponent(mountExtended);
+
+ expect(findAlert().text()).not.toBe('');
+ expect(findLink().attributes('href')).toBe(CHANGELOG_URL);
+ });
+});
diff --git a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
index d23723807b1..9df7a974af3 100644
--- a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
@@ -1,4 +1,4 @@
-import { GlModal, GlDropdown, GlDropdownItem, GlDropdownForm } from '@gitlab/ui';
+import { GlModal, GlDropdown, GlDropdownItem, GlDropdownForm, GlIcon } from '@gitlab/ui';
import { createWrapper } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@@ -29,7 +29,7 @@ describe('RegistrationDropdown', () => {
let wrapper;
const findDropdown = () => wrapper.findComponent(GlDropdown);
-
+ const findDropdownBtn = () => findDropdown().find('button');
const findRegistrationInstructionsDropdownItem = () => wrapper.findComponent(GlDropdownItem);
const findTokenDropdownItem = () => wrapper.findComponent(GlDropdownForm);
const findRegistrationToken = () => wrapper.findComponent(RegistrationToken);
@@ -90,12 +90,25 @@ describe('RegistrationDropdown', () => {
expect(wrapper.text()).toContain('Register an instance runner');
});
- it('Passes attributes to the dropdown component', () => {
+ it('Passes attributes to dropdown', () => {
createComponent({ attrs: { right: true } });
expect(findDropdown().attributes()).toMatchObject({ right: 'true' });
});
+ it('Passes default props and attributes to dropdown', () => {
+ createComponent();
+
+ expect(findDropdown().props()).toMatchObject({
+ category: 'primary',
+ variant: 'confirm',
+ });
+
+ expect(findDropdown().attributes()).toMatchObject({
+ toggleclass: '',
+ });
+ });
+
describe('Instructions dropdown item', () => {
it('Displays "Show runner" dropdown item', () => {
createComponent();
@@ -196,4 +209,51 @@ describe('RegistrationDropdown', () => {
expect(findModalContent()).toContain(newToken);
});
});
+
+ describe.each([
+ { createRunnerWorkflowForAdmin: true },
+ { createRunnerWorkflowForNamespace: true },
+ ])('When showing a "deprecated" warning', (glFeatures) => {
+ it('Passes deprecated variant props and attributes to dropdown', () => {
+ createComponent({
+ provide: { glFeatures },
+ });
+
+ expect(findDropdown().props()).toMatchObject({
+ category: 'tertiary',
+ variant: 'default',
+ text: '',
+ });
+
+ expect(findDropdown().attributes()).toMatchObject({
+ toggleclass: 'gl-px-3!',
+ });
+ });
+
+ it('shows warning text', () => {
+ createComponent(
+ {
+ provide: { glFeatures },
+ },
+ mountExtended,
+ );
+
+ const text = wrapper.findByText(s__('Runners|Support for registration tokens is deprecated'));
+
+ expect(text.exists()).toBe(true);
+ });
+
+ it('button shows only ellipsis icon', () => {
+ createComponent(
+ {
+ provide: { glFeatures },
+ },
+ mountExtended,
+ );
+
+ expect(findDropdownBtn().text()).toBe('');
+ expect(findDropdownBtn().findComponent(GlIcon).props('name')).toBe('ellipsis_v');
+ expect(findDropdownBtn().findAllComponents(GlIcon)).toHaveLength(1);
+ });
+ });
});
diff --git a/spec/frontend/ci/runner/components/registration/registration_token_spec.js b/spec/frontend/ci/runner/components/registration/registration_token_spec.js
index fc659f7974f..869c032c0b5 100644
--- a/spec/frontend/ci/runner/components/registration/registration_token_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_token_spec.js
@@ -13,13 +13,14 @@ describe('RegistrationToken', () => {
const findInputCopyToggleVisibility = () => wrapper.findComponent(InputCopyToggleVisibility);
- const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
+ const createComponent = ({ props = {}, mountFn = shallowMountExtended, ...options } = {}) => {
wrapper = mountFn(RegistrationToken, {
propsData: {
value: mockRegistrationToken,
inputId: 'token-value',
...props,
},
+ ...options,
});
showToast = wrapper.vm.$toast ? jest.spyOn(wrapper.vm.$toast, 'show') : null;
@@ -61,4 +62,23 @@ describe('RegistrationToken', () => {
expect(showToast).toHaveBeenCalledWith('Registration token copied!');
});
});
+
+ describe('When slots are used', () => {
+ const slotName = 'label-description';
+ const slotContent = 'Label Description';
+
+ beforeEach(() => {
+ createComponent({
+ slots: {
+ [slotName]: slotContent,
+ },
+ });
+ });
+
+ it('passes slots to the input component', () => {
+ const slot = findInputCopyToggleVisibility().vm.$scopedSlots[slotName];
+
+ expect(slot()[0].text).toBe(slotContent);
+ });
+ });
});
diff --git a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
index 520c9eed003..add2e58045c 100644
--- a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
@@ -2,12 +2,10 @@ import { GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import GroupRunnerRunnerApp from '~/ci/runner/group_new_runner/group_new_runner_app.vue';
import { saveAlertToLocalStorage } from '~/ci/runner/local_storage_alert/save_alert_to_local_storage';
-import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue';
import {
PARAM_KEY_PLATFORM,
@@ -17,7 +15,7 @@ import {
} from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
import { redirectTo } from '~/lib/utils/url_utility';
-import { runnerCreateResult, mockRegistrationToken } from '../mock_data';
+import { runnerCreateResult } from '../mock_data';
const mockGroupId = 'gid://gitlab/Group/72';
@@ -33,8 +31,6 @@ const mockCreatedRunner = runnerCreateResult.data.runnerCreate.runner;
describe('GroupRunnerRunnerApp', () => {
let wrapper;
- const findLegacyInstructionsLink = () => wrapper.findByTestId('legacy-instructions-link');
- const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
const findRunnerPlatformsRadioGroup = () => wrapper.findComponent(RunnerPlatformsRadioGroup);
const findRunnerCreateForm = () => wrapper.findComponent(RunnerCreateForm);
@@ -42,10 +38,6 @@ describe('GroupRunnerRunnerApp', () => {
wrapper = shallowMountExtended(GroupRunnerRunnerApp, {
propsData: {
groupId: mockGroupId,
- legacyRegistrationToken: mockRegistrationToken,
- },
- directives: {
- GlModal: createMockDirective('gl-modal'),
},
stubs: {
GlSprintf,
@@ -57,20 +49,6 @@ describe('GroupRunnerRunnerApp', () => {
createComponent();
});
- describe('Shows legacy modal', () => {
- it('passes legacy registration to modal', () => {
- expect(findRunnerInstructionsModal().props('registrationToken')).toEqual(
- mockRegistrationToken,
- );
- });
-
- it('opens a modal with the legacy instructions', () => {
- const modalId = getBinding(findLegacyInstructionsLink().element, 'gl-modal').value;
-
- expect(findRunnerInstructionsModal().props('modalId')).toBe(modalId);
- });
- });
-
describe('Platform', () => {
it('shows the platforms radio group', () => {
expect(findRunnerPlatformsRadioGroup().props('value')).toBe(DEFAULT_PLATFORM);
diff --git a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
index 701a93352ef..6949108fb1f 100644
--- a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
@@ -2,11 +2,9 @@ import { GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import ProjectRunnerRunnerApp from '~/ci/runner/project_new_runner/project_new_runner_app.vue';
-import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue';
import { PROJECT_TYPE, DEFAULT_PLATFORM } from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
@@ -26,8 +24,6 @@ const mockCreatedRunner = runnerCreateResult.data.runnerCreate.runner;
describe('ProjectRunnerRunnerApp', () => {
let wrapper;
- const findLegacyInstructionsLink = () => wrapper.findByTestId('legacy-instructions-link');
- const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
const findRunnerPlatformsRadioGroup = () => wrapper.findComponent(RunnerPlatformsRadioGroup);
const findRunnerCreateForm = () => wrapper.findComponent(RunnerCreateForm);
@@ -37,9 +33,6 @@ describe('ProjectRunnerRunnerApp', () => {
projectId: mockProjectId,
legacyRegistrationToken: mockRegistrationToken,
},
- directives: {
- GlModal: createMockDirective('gl-modal'),
- },
stubs: {
GlSprintf,
},
@@ -50,20 +43,6 @@ describe('ProjectRunnerRunnerApp', () => {
createComponent();
});
- describe('Shows legacy modal', () => {
- it('passes legacy registration to modal', () => {
- expect(findRunnerInstructionsModal().props('registrationToken')).toEqual(
- mockRegistrationToken,
- );
- });
-
- it('opens a modal with the legacy instructions', () => {
- const modalId = getBinding(findLegacyInstructionsLink().element, 'gl-modal').value;
-
- expect(findRunnerInstructionsModal().props('modalId')).toBe(modalId);
- });
- });
-
describe('Platform', () => {
it('shows the platforms radio group', () => {
expect(findRunnerPlatformsRadioGroup().props('value')).toBe(DEFAULT_PLATFORM);
diff --git a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
index 28d6b7118be..a557a1a98f3 100644
--- a/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
+++ b/spec/frontend/design_management/components/toolbar/design_navigation_spec.js
@@ -1,11 +1,10 @@
-/* global Mousetrap */
-import 'mousetrap';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlButtonGroup } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DesignNavigation from '~/design_management/components/toolbar/design_navigation.vue';
import { DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
+import { Mousetrap } from '~/lib/mousetrap';
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index f24ce8ba4ce..e58082a798f 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -1,7 +1,6 @@
import { GlLoadingIcon, GlPagination } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
-import Mousetrap from 'mousetrap';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import setWindowLocation from 'helpers/set_window_location_helper';
@@ -19,6 +18,7 @@ import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import { Mousetrap } from '~/lib/mousetrap';
import * as urlUtils from '~/lib/utils/url_utility';
import { stubPerformanceWebAPI } from 'helpers/performance';
import createDiffsStore from '../create_diffs_store';
diff --git a/spec/frontend/jobs/components/job/stages_dropdown_spec.js b/spec/frontend/jobs/components/job/stages_dropdown_spec.js
index f782d5600e6..9d01dc50e96 100644
--- a/spec/frontend/jobs/components/job/stages_dropdown_spec.js
+++ b/spec/frontend/jobs/components/job/stages_dropdown_spec.js
@@ -1,6 +1,6 @@
import { GlDropdown, GlDropdownItem, GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import Mousetrap from 'mousetrap';
+import { Mousetrap } from '~/lib/mousetrap';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import StagesDropdown from '~/jobs/components/job/sidebar/stages_dropdown.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
diff --git a/spec/frontend/lib/mousetrap_spec.js b/spec/frontend/lib/mousetrap_spec.js
new file mode 100644
index 00000000000..0ea221300a9
--- /dev/null
+++ b/spec/frontend/lib/mousetrap_spec.js
@@ -0,0 +1,113 @@
+// eslint-disable-next-line no-restricted-imports
+import Mousetrap from 'mousetrap';
+
+const originalMethodReturnValue = {};
+// Create a mock stopCallback method before ~/lib/utils/mousetrap overwrites
+// it. This allows us to spy on calls to it.
+const mockOriginalStopCallbackMethod = jest.fn().mockReturnValue(originalMethodReturnValue);
+Mousetrap.prototype.stopCallback = mockOriginalStopCallbackMethod;
+
+describe('mousetrap utils', () => {
+ describe('addStopCallback', () => {
+ let addStopCallback;
+ let clearStopCallbacksForTests;
+ const mockMousetrapInstance = { isMockMousetrap: true };
+ const mockKeyboardEvent = { type: 'keydown', key: 'Enter' };
+ const mockCombo = 'enter';
+
+ const mockKeydown = ({
+ instance = mockMousetrapInstance,
+ event = mockKeyboardEvent,
+ element = document,
+ combo = mockCombo,
+ } = {}) => Mousetrap.prototype.stopCallback.call(instance, event, element, combo);
+
+ beforeEach(async () => {
+ // Import async since it mutates the Mousetrap instance, by design.
+ ({ addStopCallback, clearStopCallbacksForTests } = await import('~/lib/mousetrap'));
+ clearStopCallbacksForTests();
+ });
+
+ it('delegates to the original stopCallback method when no additional callbacks added', () => {
+ const returnValue = mockKeydown();
+
+ expect(mockOriginalStopCallbackMethod).toHaveBeenCalledTimes(1);
+
+ const [thisArg] = mockOriginalStopCallbackMethod.mock.contexts;
+ const [eventArg, element, combo] = mockOriginalStopCallbackMethod.mock.calls[0];
+
+ expect(thisArg).toBe(mockMousetrapInstance);
+ expect(eventArg).toBe(mockKeyboardEvent);
+ expect(element).toBe(document);
+ expect(combo).toBe(mockCombo);
+
+ expect(returnValue).toBe(originalMethodReturnValue);
+ });
+
+ it('passes the expected arguments to the given stop callback', () => {
+ const callback = jest.fn();
+
+ addStopCallback(callback);
+
+ mockKeydown();
+
+ expect(callback).toHaveBeenCalledTimes(1);
+
+ const [thisArg] = callback.mock.contexts;
+ const [eventArg, element, combo] = callback.mock.calls[0];
+
+ expect(thisArg).toBe(mockMousetrapInstance);
+ expect(eventArg).toBe(mockKeyboardEvent);
+ expect(element).toBe(document);
+ expect(combo).toBe(mockCombo);
+ });
+
+ describe.each([true, false])('when a stop handler returns %p', (stopCallbackReturnValue) => {
+ let methodReturnValue;
+ const stopCallback = jest.fn().mockReturnValue(stopCallbackReturnValue);
+
+ beforeEach(() => {
+ addStopCallback(stopCallback);
+
+ methodReturnValue = mockKeydown();
+ });
+
+ it(`returns ${stopCallbackReturnValue}`, () => {
+ expect(methodReturnValue).toBe(stopCallbackReturnValue);
+ });
+
+ it('calls stop callback', () => {
+ expect(stopCallback).toHaveBeenCalledTimes(1);
+ });
+
+ it('does not call mockOriginalStopCallbackMethod', () => {
+ expect(mockOriginalStopCallbackMethod).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when a stop handler returns undefined', () => {
+ let methodReturnValue;
+ const stopCallback = jest.fn().mockReturnValue(undefined);
+
+ beforeEach(() => {
+ addStopCallback(stopCallback);
+
+ methodReturnValue = mockKeydown();
+ });
+
+ it('returns originalMethodReturnValue', () => {
+ expect(methodReturnValue).toBe(originalMethodReturnValue);
+ });
+
+ it('calls stop callback', () => {
+ expect(stopCallback).toHaveBeenCalledTimes(1);
+ });
+
+ // Because this is the only registered stop callback, the next callback
+ // is the original method.
+ it('does call original stopCallback method', () => {
+ expect(mockOriginalStopCallbackMethod).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/notes/components/discussion_navigator_spec.js b/spec/frontend/notes/components/discussion_navigator_spec.js
index 6e095f63003..14181287381 100644
--- a/spec/frontend/notes/components/discussion_navigator_spec.js
+++ b/spec/frontend/notes/components/discussion_navigator_spec.js
@@ -1,5 +1,3 @@
-/* global Mousetrap */
-import 'mousetrap';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import {
@@ -7,6 +5,7 @@ import {
MR_NEXT_UNRESOLVED_DISCUSSION,
MR_PREVIOUS_UNRESOLVED_DISCUSSION,
} from '~/behaviors/shortcuts/keybindings';
+import { Mousetrap } from '~/lib/mousetrap';
import DiscussionNavigator from '~/notes/components/discussion_navigator.vue';
import eventHub from '~/notes/event_hub';
diff --git a/spec/frontend/projects/settings/components/default_branch_selector_spec.js b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
index c1412d01b53..9baea5c5517 100644
--- a/spec/frontend/projects/settings/components/default_branch_selector_spec.js
+++ b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
@@ -28,7 +28,6 @@ describe('projects/settings/components/default_branch_selector', () => {
value: persistedDefaultBranch,
enabledRefTypes: [REF_TYPE_BRANCHES],
projectId,
- refType: null,
state: true,
toggleButtonClass: null,
translations: {
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 5e15ba26ece..290cde29866 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -22,8 +22,6 @@ import {
REF_TYPE_BRANCHES,
REF_TYPE_TAGS,
REF_TYPE_COMMITS,
- BRANCH_REF_TYPE,
- TAG_REF_TYPE,
} from '~/ref/constants';
import createStore from '~/ref/stores/';
@@ -323,7 +321,7 @@ describe('Ref selector component', () => {
describe('branches', () => {
describe('when the branches search returns results', () => {
beforeEach(() => {
- createComponent({}, { refType: BRANCH_REF_TYPE, useSymbolicRefNames: true });
+ createComponent({}, { useSymbolicRefNames: true });
return waitForRequests();
});
@@ -386,7 +384,7 @@ describe('Ref selector component', () => {
describe('tags', () => {
describe('when the tags search returns results', () => {
beforeEach(() => {
- createComponent({}, { refType: TAG_REF_TYPE, useSymbolicRefNames: true });
+ createComponent({}, { useSymbolicRefNames: true });
return waitForRequests();
});
diff --git a/spec/frontend/shortcuts_spec.js b/spec/frontend/shortcuts_spec.js
index d1371ca0ef9..4e74bdc5895 100644
--- a/spec/frontend/shortcuts_spec.js
+++ b/spec/frontend/shortcuts_spec.js
@@ -1,16 +1,8 @@
import $ from 'jquery';
import { flatten } from 'lodash';
+import { Mousetrap } from '~/lib/mousetrap';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import Shortcuts from '~/behaviors/shortcuts/shortcuts';
-
-const mockMousetrap = {
- bind: jest.fn(),
- unbind: jest.fn(),
-};
-
-jest.mock('mousetrap', () => {
- return jest.fn().mockImplementation(() => mockMousetrap);
-});
+import Shortcuts, { LOCAL_MOUSETRAP_DATA_KEY } from '~/behaviors/shortcuts/shortcuts';
jest.mock('mousetrap/plugins/pause/mousetrap-pause', () => {});
@@ -20,6 +12,11 @@ describe('Shortcuts', () => {
$.Event(type, {
target,
});
+ let shortcuts;
+
+ beforeAll(() => {
+ shortcuts = new Shortcuts();
+ });
beforeEach(() => {
loadHTMLFixture(fixtureName);
@@ -28,7 +25,9 @@ describe('Shortcuts', () => {
jest.spyOn(document.querySelector('.edit-note .js-md-preview-button'), 'focus');
jest.spyOn(document.querySelector('#search'), 'focus');
- new Shortcuts(); // eslint-disable-line no-new
+ jest.spyOn(Mousetrap.prototype, 'stopCallback');
+ jest.spyOn(Mousetrap.prototype, 'bind').mockImplementation();
+ jest.spyOn(Mousetrap.prototype, 'unbind').mockImplementation();
});
afterEach(() => {
@@ -61,7 +60,7 @@ describe('Shortcuts', () => {
});
describe('markdown shortcuts', () => {
- let shortcuts;
+ let shortcutElements;
beforeEach(() => {
// Get all shortcuts specified with md-shortcuts attributes in the fixture.
@@ -71,7 +70,7 @@ describe('Shortcuts', () => {
// [ 'mod+i' ],
// [ 'mod+k' ]
// ]
- shortcuts = $('.edit-note .js-md')
+ shortcutElements = $('.edit-note .js-md')
.map(function getShortcutsFromToolbarBtn() {
const mdShortcuts = $(this).data('md-shortcuts');
@@ -83,19 +82,26 @@ describe('Shortcuts', () => {
});
describe('initMarkdownEditorShortcuts', () => {
+ let $textarea;
+ let localMousetrapInstance;
+
beforeEach(() => {
- Shortcuts.initMarkdownEditorShortcuts($('.edit-note textarea'));
+ $textarea = $('.edit-note textarea');
+ Shortcuts.initMarkdownEditorShortcuts($textarea);
+ localMousetrapInstance = $textarea.data(LOCAL_MOUSETRAP_DATA_KEY);
});
it('attaches a Mousetrap handler for every markdown shortcut specified with md-shortcuts', () => {
- const expectedCalls = shortcuts.map((s) => [s, expect.any(Function)]);
+ const expectedCalls = shortcutElements.map((s) => [s, expect.any(Function)]);
- expect(mockMousetrap.bind.mock.calls).toEqual(expectedCalls);
+ expect(Mousetrap.prototype.bind.mock.calls).toEqual(expectedCalls);
});
it('attaches a stopCallback that allows each markdown shortcut specified with md-shortcuts', () => {
- flatten(shortcuts).forEach((s) => {
- expect(mockMousetrap.stopCallback(null, null, s)).toBe(false);
+ flatten(shortcutElements).forEach((s) => {
+ expect(
+ localMousetrapInstance.stopCallback.call(localMousetrapInstance, null, null, s),
+ ).toBe(false);
});
});
});
@@ -104,16 +110,16 @@ describe('Shortcuts', () => {
it('does nothing if initMarkdownEditorShortcuts was not previous called', () => {
Shortcuts.removeMarkdownEditorShortcuts($('.edit-note textarea'));
- expect(mockMousetrap.unbind.mock.calls).toEqual([]);
+ expect(Mousetrap.prototype.unbind.mock.calls).toEqual([]);
});
it('removes Mousetrap handlers for every markdown shortcut specified with md-shortcuts', () => {
Shortcuts.initMarkdownEditorShortcuts($('.edit-note textarea'));
Shortcuts.removeMarkdownEditorShortcuts($('.edit-note textarea'));
- const expectedCalls = shortcuts.map((s) => [s]);
+ const expectedCalls = shortcutElements.map((s) => [s]);
- expect(mockMousetrap.unbind.mock.calls).toEqual(expectedCalls);
+ expect(Mousetrap.prototype.unbind.mock.calls).toEqual(expectedCalls);
});
});
});
@@ -136,4 +142,35 @@ describe('Shortcuts', () => {
});
});
});
+
+ describe('bindCommand(s)', () => {
+ it('bindCommand calls Mousetrap.bind correctly', () => {
+ const mockCommand = { defaultKeys: ['m'] };
+ const mockCallback = () => {};
+
+ shortcuts.bindCommand(mockCommand, mockCallback);
+
+ expect(Mousetrap.prototype.bind).toHaveBeenCalledTimes(1);
+ const [callArguments] = Mousetrap.prototype.bind.mock.calls;
+ expect(callArguments[0]).toEqual(mockCommand.defaultKeys);
+ expect(callArguments[1]).toBe(mockCallback);
+ });
+
+ it('bindCommands calls Mousetrap.bind correctly', () => {
+ const mockCommandsAndCallbacks = [
+ [{ defaultKeys: ['1'] }, () => {}],
+ [{ defaultKeys: ['2'] }, () => {}],
+ ];
+
+ shortcuts.bindCommands(mockCommandsAndCallbacks);
+
+ expect(Mousetrap.prototype.bind).toHaveBeenCalledTimes(mockCommandsAndCallbacks.length);
+ const { calls } = Mousetrap.prototype.bind.mock;
+
+ mockCommandsAndCallbacks.forEach(([mockCommand, mockCallback], i) => {
+ expect(calls[i][0]).toEqual(mockCommand.defaultKeys);
+ expect(calls[i][1]).toBe(mockCallback);
+ });
+ });
+ });
});
diff --git a/spec/frontend/super_sidebar/components/super_sidebar_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
index 2d535b0c727..16b8dfd211f 100644
--- a/spec/frontend/super_sidebar/components/super_sidebar_spec.js
+++ b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
@@ -1,5 +1,5 @@
import { nextTick } from 'vue';
-import Mousetrap from 'mousetrap';
+import { Mousetrap } from '~/lib/mousetrap';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SuperSidebar from '~/super_sidebar/components/super_sidebar.vue';
import HelpCenter from '~/super_sidebar/components/help_center.vue';
diff --git a/spec/frontend/vue_shared/components/file_finder/index_spec.js b/spec/frontend/vue_shared/components/file_finder/index_spec.js
index 9708d689245..bb0b12d205a 100644
--- a/spec/frontend/vue_shared/components/file_finder/index_spec.js
+++ b/spec/frontend/vue_shared/components/file_finder/index_spec.js
@@ -1,7 +1,7 @@
import { GlLoadingIcon } from '@gitlab/ui';
-import Mousetrap from 'mousetrap';
import { nextTick } from 'vue';
import VirtualList from 'vue-virtual-scroll-list';
+import { Mousetrap } from '~/lib/mousetrap';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { file } from 'jest/ide/helpers';
import FindFileComponent from '~/vue_shared/components/file_finder/index.vue';
diff --git a/spec/frontend/zen_mode_spec.js b/spec/frontend/zen_mode_spec.js
index 025a92464f1..3e66380ac46 100644
--- a/spec/frontend/zen_mode_spec.js
+++ b/spec/frontend/zen_mode_spec.js
@@ -2,7 +2,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Dropzone from 'dropzone';
import $ from 'jquery';
-import Mousetrap from 'mousetrap';
+import { Mousetrap } from '~/lib/mousetrap';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import GLForm from '~/gl_form';
import * as utils from '~/lib/utils/common_utils';