summaryrefslogtreecommitdiff
path: root/spec/frontend/ide
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide')
-rw-r--r--spec/frontend/ide/components/ide_spec.js31
-rw-r--r--spec/frontend/ide/components/panes/collapsible_sidebar_spec.js26
-rw-r--r--spec/frontend/ide/components/panes/right_spec.js45
-rw-r--r--spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js214
-rw-r--r--spec/frontend/ide/stores/mutations_spec.js2
5 files changed, 310 insertions, 8 deletions
diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js
index 48c670757a2..a575f428a69 100644
--- a/spec/frontend/ide/components/ide_spec.js
+++ b/spec/frontend/ide/components/ide_spec.js
@@ -3,9 +3,11 @@ import Vue from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import { stubPerformanceWebAPI } from 'helpers/performance';
+import { __ } from '~/locale';
import CannotPushCodeAlert from '~/ide/components/cannot_push_code_alert.vue';
import ErrorMessage from '~/ide/components/error_message.vue';
import Ide from '~/ide/components/ide.vue';
+import eventHub from '~/ide/eventhub';
import { MSG_CANNOT_PUSH_CODE_GO_TO_FORK, MSG_GO_TO_FORK } from '~/ide/messages';
import { createStore } from '~/ide/stores';
import { file } from '../helpers';
@@ -14,6 +16,7 @@ import { projectData } from '../mock_data';
Vue.use(Vuex);
const TEST_FORK_IDE_PATH = '/test/ide/path';
+const MSG_ARE_YOU_SURE = __('Are you sure you want to lose unsaved changes?');
describe('WebIDE', () => {
const emptyProjData = { ...projectData, empty_repo: true, branches: {} };
@@ -40,6 +43,8 @@ describe('WebIDE', () => {
const findAlert = () => wrapper.findComponent(CannotPushCodeAlert);
+ const callOnBeforeUnload = (e = {}) => window.onbeforeunload(e);
+
beforeEach(() => {
stubPerformanceWebAPI();
@@ -49,6 +54,7 @@ describe('WebIDE', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
+ window.onbeforeunload = null;
});
describe('ide component, empty repo', () => {
@@ -90,7 +96,8 @@ describe('WebIDE', () => {
describe('onBeforeUnload', () => {
it('returns undefined when no staged files or changed files', () => {
createComponent();
- expect(wrapper.vm.onBeforeUnload()).toBe(undefined);
+
+ expect(callOnBeforeUnload()).toBe(undefined);
});
it('returns warning text when their are changed files', () => {
@@ -100,7 +107,10 @@ describe('WebIDE', () => {
},
});
- expect(wrapper.vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?');
+ const e = {};
+
+ expect(callOnBeforeUnload(e)).toBe(MSG_ARE_YOU_SURE);
+ expect(e.returnValue).toBe(MSG_ARE_YOU_SURE);
});
it('returns warning text when their are staged files', () => {
@@ -110,20 +120,27 @@ describe('WebIDE', () => {
},
});
- expect(wrapper.vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?');
+ const e = {};
+
+ expect(callOnBeforeUnload(e)).toBe(MSG_ARE_YOU_SURE);
+ expect(e.returnValue).toBe(MSG_ARE_YOU_SURE);
});
- it('updates event object', () => {
- const event = {};
+ it('returns undefined once after "skip-beforeunload" was emitted', () => {
createComponent({
state: {
stagedFiles: [file()],
},
});
- wrapper.vm.onBeforeUnload(event);
+ eventHub.$emit('skip-beforeunload');
+ const e = {};
+
+ expect(callOnBeforeUnload()).toBe(undefined);
+ expect(e.returnValue).toBe(undefined);
- expect(event.returnValue).toBe('Are you sure you want to lose unsaved changes?');
+ expect(callOnBeforeUnload(e)).toBe(MSG_ARE_YOU_SURE);
+ expect(e.returnValue).toBe(MSG_ARE_YOU_SURE);
});
});
diff --git a/spec/frontend/ide/components/panes/collapsible_sidebar_spec.js b/spec/frontend/ide/components/panes/collapsible_sidebar_spec.js
index 1d38231a767..e92f843ae6e 100644
--- a/spec/frontend/ide/components/panes/collapsible_sidebar_spec.js
+++ b/spec/frontend/ide/components/panes/collapsible_sidebar_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import IdeSidebarNav from '~/ide/components/ide_sidebar_nav.vue';
import CollapsibleSidebar from '~/ide/components/panes/collapsible_sidebar.vue';
@@ -127,5 +127,29 @@ describe('ide/components/panes/collapsible_sidebar.vue', () => {
});
});
});
+
+ describe('with initOpenView that does not exist', () => {
+ beforeEach(async () => {
+ createComponent({ extensionTabs, initOpenView: 'does-not-exist' });
+
+ await nextTick();
+ });
+
+ it('nothing is dispatched', () => {
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('with initOpenView that does exist', () => {
+ beforeEach(async () => {
+ createComponent({ extensionTabs, initOpenView: fakeView.name });
+
+ await nextTick();
+ });
+
+ it('dispatches open with view on create', () => {
+ expect(store.dispatch).toHaveBeenCalledWith('rightPane/open', fakeView);
+ });
+ });
});
});
diff --git a/spec/frontend/ide/components/panes/right_spec.js b/spec/frontend/ide/components/panes/right_spec.js
index 4555f519bc2..b7349b8fed1 100644
--- a/spec/frontend/ide/components/panes/right_spec.js
+++ b/spec/frontend/ide/components/panes/right_spec.js
@@ -3,12 +3,16 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import CollapsibleSidebar from '~/ide/components/panes/collapsible_sidebar.vue';
import RightPane from '~/ide/components/panes/right.vue';
+import SwitchEditorsView from '~/ide/components/switch_editors/switch_editors_view.vue';
import { rightSidebarViews } from '~/ide/constants';
import { createStore } from '~/ide/stores';
import extendStore from '~/ide/stores/extend';
+import { __ } from '~/locale';
Vue.use(Vuex);
+const SWITCH_EDITORS_VIEW_NAME = 'switch-editors';
+
describe('ide/components/panes/right.vue', () => {
let wrapper;
let store;
@@ -33,6 +37,19 @@ describe('ide/components/panes/right.vue', () => {
wrapper = null;
});
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders collapsible-sidebar', () => {
+ expect(wrapper.findComponent(CollapsibleSidebar).props()).toMatchObject({
+ side: 'right',
+ initOpenView: SWITCH_EDITORS_VIEW_NAME,
+ });
+ });
+ });
+
describe('pipelines tab', () => {
it('is always shown', () => {
createComponent();
@@ -113,4 +130,32 @@ describe('ide/components/panes/right.vue', () => {
);
});
});
+
+ describe('switch editors tab', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it.each`
+ desc | canUseNewWebIde | expectedShow
+ ${'is shown'} | ${true} | ${true}
+ ${'is not shown'} | ${false} | ${false}
+ `('with canUseNewWebIde=$canUseNewWebIde, $desc', async ({ canUseNewWebIde, expectedShow }) => {
+ Object.assign(store.state, { canUseNewWebIde });
+
+ await nextTick();
+
+ expect(wrapper.findComponent(CollapsibleSidebar).props('extensionTabs')).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ show: expectedShow,
+ title: __('Switch editors'),
+ views: [
+ { component: SwitchEditorsView, name: SWITCH_EDITORS_VIEW_NAME, keepAlive: true },
+ ],
+ }),
+ ]),
+ );
+ });
+ });
});
diff --git a/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js b/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js
new file mode 100644
index 00000000000..7a958391fea
--- /dev/null
+++ b/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js
@@ -0,0 +1,214 @@
+import { GlButton, GlEmptyState, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+import { createAlert } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { logError } from '~/lib/logger';
+import { __ } from '~/locale';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
+import SwitchEditorsView, {
+ MSG_ERROR_ALERT,
+ MSG_CONFIRM,
+ MSG_TITLE,
+ MSG_LEARN_MORE,
+ MSG_DESCRIPTION,
+} from '~/ide/components/switch_editors/switch_editors_view.vue';
+import eventHub from '~/ide/eventhub';
+import { createStore } from '~/ide/stores';
+
+jest.mock('~/flash');
+jest.mock('~/lib/logger');
+jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
+
+const TEST_USER_PREFERENCES_PATH = '/test/user-pref/path';
+const TEST_SWITCH_EDITOR_SVG_PATH = '/test/switch/editor/path.svg';
+const TEST_HREF = '/test/new/web/ide/href';
+
+describe('~/ide/components/switch_editors/switch_editors_view.vue', () => {
+ useMockLocationHelper();
+
+ let store;
+ let wrapper;
+ let confirmResolve;
+ let requestSpy;
+ let skipBeforeunloadSpy;
+ let axiosMock;
+
+ // region: finders ------------------
+ const findButton = () => wrapper.findComponent(GlButton);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+
+ // region: actions ------------------
+ const triggerSwitchPreference = () => findButton().vm.$emit('click');
+ const submitConfirm = async (val) => {
+ confirmResolve(val);
+
+ // why: We need to wait for promises for the immediate next lines to be executed
+ await waitForPromises();
+ };
+
+ const createComponent = () => {
+ wrapper = shallowMount(SwitchEditorsView, {
+ store,
+ stubs: {
+ GlEmptyState,
+ },
+ });
+ };
+
+ // region: test setup ------------------
+ beforeEach(() => {
+ // Setup skip-beforeunload side-effect
+ skipBeforeunloadSpy = jest.fn();
+ eventHub.$on('skip-beforeunload', skipBeforeunloadSpy);
+
+ // Setup request side-effect
+ requestSpy = jest.fn().mockImplementation(() => new Promise(() => {}));
+ axiosMock = new MockAdapter(axios);
+ axiosMock.onPut(TEST_USER_PREFERENCES_PATH).reply(({ data }) => requestSpy(data));
+
+ // Setup store
+ store = createStore();
+ store.state.userPreferencesPath = TEST_USER_PREFERENCES_PATH;
+ store.state.switchEditorSvgPath = TEST_SWITCH_EDITOR_SVG_PATH;
+ store.state.links = {
+ newWebIDEHelpPagePath: TEST_HREF,
+ };
+
+ // Setup user confirm side-effect
+ confirmAction.mockImplementation(
+ () =>
+ new Promise((resolve) => {
+ confirmResolve = resolve;
+ }),
+ );
+ });
+
+ afterEach(() => {
+ eventHub.$off('skip-beforeunload', skipBeforeunloadSpy);
+
+ axiosMock.restore();
+ });
+
+ // region: tests ------------------
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('render empty state', () => {
+ expect(findEmptyState().props()).toMatchObject({
+ svgPath: TEST_SWITCH_EDITOR_SVG_PATH,
+ svgHeight: 150,
+ title: MSG_TITLE,
+ });
+ });
+
+ it('render link', () => {
+ expect(wrapper.findComponent(GlLink).attributes('href')).toBe(TEST_HREF);
+ expect(wrapper.findComponent(GlLink).text()).toBe(MSG_LEARN_MORE);
+ });
+
+ it('renders description', () => {
+ expect(findEmptyState().text()).toContain(MSG_DESCRIPTION);
+ });
+
+ it('is not loading', () => {
+ expect(findButton().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when user triggers switch preference', () => {
+ beforeEach(() => {
+ createComponent();
+
+ triggerSwitchPreference();
+ });
+
+ it('creates a single confirm', () => {
+ // Call again to ensure that we only show 1 confirm action
+ triggerSwitchPreference();
+
+ expect(confirmAction).toHaveBeenCalledTimes(1);
+ expect(confirmAction).toHaveBeenCalledWith(MSG_CONFIRM, {
+ primaryBtnText: __('Switch editors'),
+ cancelBtnText: __('Cancel'),
+ });
+ });
+
+ it('starts loading', () => {
+ expect(findButton().props('loading')).toBe(true);
+ });
+
+ describe('when user cancels confirm', () => {
+ beforeEach(async () => {
+ await submitConfirm(false);
+ });
+
+ it('does not make request', () => {
+ expect(requestSpy).not.toHaveBeenCalled();
+ });
+
+ it('can be triggered again', () => {
+ triggerSwitchPreference();
+
+ expect(confirmAction).toHaveBeenCalledTimes(2);
+ });
+ });
+
+ describe('when user accepts confirm and response success', () => {
+ beforeEach(async () => {
+ requestSpy.mockReturnValue([200, {}]);
+ await submitConfirm(true);
+ });
+
+ it('does not handle error', () => {
+ expect(logError).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('emits "skip-beforeunload" and reloads', () => {
+ expect(skipBeforeunloadSpy).toHaveBeenCalledTimes(1);
+ expect(window.location.reload).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls request', () => {
+ expect(requestSpy).toHaveBeenCalledTimes(1);
+ expect(requestSpy).toHaveBeenCalledWith(
+ JSON.stringify({ user: { use_legacy_web_ide: false } }),
+ );
+ });
+
+ it('is not loading', () => {
+ expect(findButton().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when user accepts confirm and response fails', () => {
+ beforeEach(async () => {
+ requestSpy.mockReturnValue([400, {}]);
+ await submitConfirm(true);
+ });
+
+ it('handles error', () => {
+ expect(logError).toHaveBeenCalledTimes(1);
+ expect(logError).toHaveBeenCalledWith(
+ 'Error while updating user preferences',
+ expect.any(Error),
+ );
+
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({
+ message: MSG_ERROR_ALERT,
+ });
+ });
+
+ it('does not reload', () => {
+ expect(skipBeforeunloadSpy).not.toHaveBeenCalled();
+ expect(window.location.reload).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js
index 4117f2648bd..ae21d257bb2 100644
--- a/spec/frontend/ide/stores/mutations_spec.js
+++ b/spec/frontend/ide/stores/mutations_spec.js
@@ -87,11 +87,13 @@ describe('Multi-file store mutations', () => {
emptyStateSvgPath: 'emptyState',
noChangesStateSvgPath: 'noChanges',
committedStateSvgPath: 'committed',
+ switchEditorSvgPath: 'switchEditorSvg',
});
expect(localState.emptyStateSvgPath).toBe('emptyState');
expect(localState.noChangesStateSvgPath).toBe('noChanges');
expect(localState.committedStateSvgPath).toBe('committed');
+ expect(localState.switchEditorSvgPath).toBe('switchEditorSvg');
});
});