summaryrefslogtreecommitdiff
path: root/spec/frontend/ide
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide')
-rw-r--r--spec/frontend/ide/components/panes/right_spec.js33
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js2
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js94
-rw-r--r--spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js214
-rw-r--r--spec/frontend/ide/init_gitlab_web_ide_spec.js164
-rw-r--r--spec/frontend/ide/lib/common/model_spec.js9
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js22
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js32
-rw-r--r--spec/frontend/ide/remote/index_spec.js91
-rw-r--r--spec/frontend/ide/services/index_spec.js2
-rw-r--r--spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js4
-rw-r--r--spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js4
-rw-r--r--spec/frontend/ide/stores/modules/terminal/messages_spec.js4
13 files changed, 392 insertions, 283 deletions
diff --git a/spec/frontend/ide/components/panes/right_spec.js b/spec/frontend/ide/components/panes/right_spec.js
index b7349b8fed1..294f5eee863 100644
--- a/spec/frontend/ide/components/panes/right_spec.js
+++ b/spec/frontend/ide/components/panes/right_spec.js
@@ -3,16 +3,12 @@ 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;
@@ -45,7 +41,6 @@ describe('ide/components/panes/right.vue', () => {
it('renders collapsible-sidebar', () => {
expect(wrapper.findComponent(CollapsibleSidebar).props()).toMatchObject({
side: 'right',
- initOpenView: SWITCH_EDITORS_VIEW_NAME,
});
});
});
@@ -130,32 +125,4 @@ 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/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index 545924c9c11..d82b97561f0 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -185,7 +185,7 @@ describe('IDE pipelines list', () => {
},
);
- expect(wrapper.text()).toContain('Found errors in your .gitlab-ci.yml:');
+ expect(wrapper.text()).toContain('Unable to create pipeline');
expect(wrapper.text()).toContain(yamlError);
});
});
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 9921d8cba18..211fee31a9c 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -4,13 +4,17 @@ import { editor as monacoEditor, Range } from 'monaco-editor';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
-import '~/behaviors/markdown/render_gfm';
import waitForPromises from 'helpers/wait_for_promises';
import { stubPerformanceWebAPI } from 'helpers/performance';
import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
-import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants';
+import {
+ EDITOR_CODE_INSTANCE_FN,
+ EDITOR_DIFF_INSTANCE_FN,
+ EXTENSION_CI_SCHEMA_FILE_NAME_MATCH,
+} from '~/editor/constants';
import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
import { EditorMarkdownPreviewExtension } from '~/editor/extensions/source_editor_markdown_livepreview_ext';
+import { CiSchemaExtension } from '~/editor/extensions/source_editor_ci_schema_ext';
import SourceEditor from '~/editor/source_editor';
import RepoEditor from '~/ide/components/repo_editor.vue';
import { leftSidebarViews, FILE_VIEW_MODE_PREVIEW, viewerTypes } from '~/ide/constants';
@@ -22,6 +26,9 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer
import SourceEditorInstance from '~/editor/source_editor_instance';
import { file } from '../helpers';
+jest.mock('~/behaviors/markdown/render_gfm');
+jest.mock('~/editor/extensions/source_editor_ci_schema_ext');
+
const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';
const CURRENT_PROJECT_ID = 'gitlab-org/gitlab';
@@ -46,6 +53,12 @@ const dummyFile = {
tempFile: true,
active: true,
},
+ ciConfig: {
+ ...file(EXTENSION_CI_SCHEMA_FILE_NAME_MATCH),
+ content: '',
+ tempFile: true,
+ active: true,
+ },
empty: {
...file('empty'),
tempFile: false,
@@ -101,6 +114,7 @@ describe('RepoEditor', () => {
let createDiffInstanceSpy;
let createModelSpy;
let applyExtensionSpy;
+ let removeExtensionSpy;
let extensionsStore;
const waitForEditorSetup = () =>
@@ -108,7 +122,7 @@ describe('RepoEditor', () => {
vm.$once('editorSetup', resolve);
});
- const createComponent = async ({ state = {}, activeFile = dummyFile.text } = {}) => {
+ const createComponent = async ({ state = {}, activeFile = dummyFile.text, flags = {} } = {}) => {
const store = prepareStore(state, activeFile);
wrapper = shallowMount(RepoEditor, {
store,
@@ -118,6 +132,9 @@ describe('RepoEditor', () => {
mocks: {
ContentViewer,
},
+ provide: {
+ glFeatures: flags,
+ },
});
await waitForPromises();
vm = wrapper.vm;
@@ -137,6 +154,7 @@ describe('RepoEditor', () => {
createDiffInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_DIFF_INSTANCE_FN);
createModelSpy = jest.spyOn(monacoEditor, 'createModel');
applyExtensionSpy = jest.spyOn(SourceEditorInstance.prototype, 'use');
+ removeExtensionSpy = jest.spyOn(SourceEditorInstance.prototype, 'unuse');
jest.spyOn(service, 'getFileData').mockResolvedValue();
jest.spyOn(service, 'getRawFileData').mockResolvedValue();
});
@@ -177,6 +195,76 @@ describe('RepoEditor', () => {
});
});
+ describe('schema registration for .gitlab-ci.yml', () => {
+ const setup = async (activeFile, flagIsOn = true) => {
+ await createComponent({
+ flags: {
+ schemaLinting: flagIsOn,
+ },
+ });
+ vm.editor.registerCiSchema = jest.fn();
+ if (activeFile) {
+ wrapper.setProps({ file: activeFile });
+ }
+ await waitForPromises();
+ await nextTick();
+ };
+ it.each`
+ flagIsOn | activeFile | shouldUseExtension | desc
+ ${false} | ${dummyFile.markdown} | ${false} | ${`file is not CI config; should NOT`}
+ ${true} | ${dummyFile.markdown} | ${false} | ${`file is not CI config; should NOT`}
+ ${false} | ${dummyFile.ciConfig} | ${false} | ${`file is CI config; should NOT`}
+ ${true} | ${dummyFile.ciConfig} | ${true} | ${`file is CI config; should`}
+ `(
+ 'when the flag is "$flagIsOn", $desc use extension',
+ async ({ flagIsOn, activeFile, shouldUseExtension }) => {
+ await setup(activeFile, flagIsOn);
+
+ if (shouldUseExtension) {
+ expect(applyExtensionSpy).toHaveBeenCalledWith({
+ definition: CiSchemaExtension,
+ });
+ } else {
+ expect(applyExtensionSpy).not.toHaveBeenCalledWith({
+ definition: CiSchemaExtension,
+ });
+ }
+ },
+ );
+ it('stores the fetched extension and does not double-fetch the schema', async () => {
+ await setup();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(0);
+
+ wrapper.setProps({ file: dummyFile.ciConfig });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.CiSchemaExtension).toEqual(CiSchemaExtension);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(1);
+
+ wrapper.setProps({ file: dummyFile.markdown });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(1);
+
+ wrapper.setProps({ file: dummyFile.ciConfig });
+ await waitForPromises();
+ await nextTick();
+ expect(CiSchemaExtension).toHaveBeenCalledTimes(1);
+ expect(vm.editor.registerCiSchema).toHaveBeenCalledTimes(2);
+ });
+ it('unuses the existing CI extension if the new model is not CI config', async () => {
+ await setup(dummyFile.ciConfig);
+
+ expect(removeExtensionSpy).not.toHaveBeenCalled();
+ wrapper.setProps({ file: dummyFile.markdown });
+ await waitForPromises();
+ await nextTick();
+ expect(removeExtensionSpy).toHaveBeenCalledWith(CiSchemaExtension);
+ });
+ });
+
describe('when file is markdown', () => {
let mock;
let activeFile;
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
deleted file mode 100644
index 7a958391fea..00000000000
--- a/spec/frontend/ide/components/switch_editors/switch_editors_view_spec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-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/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js
index 067da25cb52..97254ab680b 100644
--- a/spec/frontend/ide/init_gitlab_web_ide_spec.js
+++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js
@@ -1,62 +1,190 @@
import { start } from '@gitlab/web-ide';
+import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from '~/ide/constants';
import { initGitlabWebIDE } from '~/ide/init_gitlab_web_ide';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_action';
+import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form';
import { TEST_HOST } from 'helpers/test_constants';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import waitForPromises from 'helpers/wait_for_promises';
jest.mock('@gitlab/web-ide');
+jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_action');
+jest.mock('~/lib/utils/create_and_submit_form');
+jest.mock('~/lib/utils/csrf', () => ({
+ token: 'mock-csrf-token',
+ headerKey: 'mock-csrf-header',
+}));
const ROOT_ELEMENT_ID = 'ide';
const TEST_NONCE = 'test123nonce';
const TEST_PROJECT_PATH = 'group1/project1';
const TEST_BRANCH_NAME = '12345-foo-patch';
const TEST_GITLAB_URL = 'https://test-gitlab/';
+const TEST_USER_PREFERENCES_PATH = '/user/preferences';
const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path';
+const TEST_FILE_PATH = 'foo/README.md';
+const TEST_MR_ID = '7';
+const TEST_MR_TARGET_PROJECT = 'gitlab-org/the-real-gitlab';
+const TEST_FORK_INFO = { fork_path: '/forky' };
+const TEST_IDE_REMOTE_PATH = '/-/ide/remote/:remote_host/:remote_path';
+const TEST_START_REMOTE_PARAMS = {
+ remoteHost: 'dev.example.gitlab.com/test',
+ remotePath: '/test/projects/f oo',
+ connectionToken: '123abc',
+};
describe('ide/init_gitlab_web_ide', () => {
+ let resolveConfirm;
+
const createRootElement = () => {
const el = document.createElement('div');
el.id = ROOT_ELEMENT_ID;
// why: We'll test that this class is removed later
- el.classList.add('ide-loading');
+ el.classList.add('test-class');
el.dataset.projectPath = TEST_PROJECT_PATH;
el.dataset.cspNonce = TEST_NONCE;
el.dataset.branchName = TEST_BRANCH_NAME;
+ el.dataset.ideRemotePath = TEST_IDE_REMOTE_PATH;
+ el.dataset.userPreferencesPath = TEST_USER_PREFERENCES_PATH;
+ el.dataset.mergeRequest = TEST_MR_ID;
+ el.dataset.filePath = TEST_FILE_PATH;
document.body.append(el);
};
const findRootElement = () => document.getElementById(ROOT_ELEMENT_ID);
- const act = () => initGitlabWebIDE(findRootElement());
+ const createSubject = () => initGitlabWebIDE(findRootElement());
+ const triggerHandleStartRemote = (startRemoteParams) => {
+ const [, config] = start.mock.calls[0];
+
+ config.handleStartRemote(startRemoteParams);
+ };
beforeEach(() => {
process.env.GITLAB_WEB_IDE_PUBLIC_PATH = TEST_GITLAB_WEB_IDE_PUBLIC_PATH;
window.gon.gitlab_url = TEST_GITLAB_URL;
- createRootElement();
+ confirmAction.mockImplementation(
+ () =>
+ new Promise((resolve) => {
+ resolveConfirm = resolve;
+ }),
+ );
- act();
+ createRootElement();
});
afterEach(() => {
document.body.innerHTML = '';
});
- it('calls start with element', () => {
- expect(start).toHaveBeenCalledWith(findRootElement(), {
- baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
- projectPath: TEST_PROJECT_PATH,
- ref: TEST_BRANCH_NAME,
- gitlabUrl: TEST_GITLAB_URL,
- nonce: TEST_NONCE,
+ describe('default', () => {
+ beforeEach(() => {
+ createSubject();
+ });
+
+ it('calls start with element', () => {
+ expect(start).toHaveBeenCalledTimes(1);
+ expect(start).toHaveBeenCalledWith(findRootElement(), {
+ baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
+ projectPath: TEST_PROJECT_PATH,
+ ref: TEST_BRANCH_NAME,
+ filePath: TEST_FILE_PATH,
+ mrId: TEST_MR_ID,
+ mrTargetProject: '',
+ forkInfo: null,
+ gitlabUrl: TEST_GITLAB_URL,
+ nonce: TEST_NONCE,
+ httpHeaders: {
+ 'mock-csrf-header': 'mock-csrf-token',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ links: {
+ userPreferences: TEST_USER_PREFERENCES_PATH,
+ feedbackIssue: GITLAB_WEB_IDE_FEEDBACK_ISSUE,
+ },
+ handleStartRemote: expect.any(Function),
+ });
+ });
+
+ it('clears classes and data from root element', () => {
+ const rootEl = findRootElement();
+
+ // why: Snapshot to test that the element was cleaned including `test-class`
+ expect(rootEl.outerHTML).toBe(
+ '<div id="ide" class="gl--flex-center gl-relative gl-h-full"></div>',
+ );
+ });
+
+ describe('when handleStartRemote is triggered', () => {
+ beforeEach(() => {
+ triggerHandleStartRemote(TEST_START_REMOTE_PARAMS);
+ });
+
+ it('promts for confirm', () => {
+ expect(confirmAction).toHaveBeenCalledWith(expect.any(String), {
+ primaryBtnText: expect.any(String),
+ cancelBtnText: expect.any(String),
+ });
+ });
+
+ it('does not submit, when not confirmed', async () => {
+ resolveConfirm(false);
+
+ await waitForPromises();
+
+ expect(createAndSubmitForm).not.toHaveBeenCalled();
+ });
+
+ it('submits, when confirmed', async () => {
+ resolveConfirm(true);
+
+ await waitForPromises();
+
+ expect(createAndSubmitForm).toHaveBeenCalledWith({
+ url: '/-/ide/remote/dev.example.gitlab.com%2Ftest/test/projects/f%20oo',
+ data: {
+ connection_token: TEST_START_REMOTE_PARAMS.connectionToken,
+ return_url: window.location.href,
+ },
+ });
+ });
+ });
+ });
+
+ describe('when URL has target_project in query params', () => {
+ beforeEach(() => {
+ setWindowLocation(
+ `https://example.com/-/ide?target_project=${encodeURIComponent(TEST_MR_TARGET_PROJECT)}`,
+ );
+
+ createSubject();
+ });
+
+ it('includes mrTargetProject', () => {
+ expect(start).toHaveBeenCalledWith(
+ findRootElement(),
+ expect.objectContaining({
+ mrTargetProject: TEST_MR_TARGET_PROJECT,
+ }),
+ );
});
});
- it('clears classes and data from root element', () => {
- const rootEl = findRootElement();
+ describe('when forkInfo is in dataset', () => {
+ beforeEach(() => {
+ findRootElement().dataset.forkInfo = JSON.stringify(TEST_FORK_INFO);
- // why: Snapshot to test that `ide-loading` was removed and no other
- // artifacts are remaining.
- expect(rootEl.outerHTML).toBe(
- '<div id="ide" class="gl--flex-center gl-relative gl-h-full"></div>',
- );
+ createSubject();
+ });
+
+ it('includes forkInfo', () => {
+ expect(start).toHaveBeenCalledWith(
+ findRootElement(),
+ expect.objectContaining({
+ forkInfo: TEST_FORK_INFO,
+ }),
+ );
+ });
});
});
diff --git a/spec/frontend/ide/lib/common/model_spec.js b/spec/frontend/ide/lib/common/model_spec.js
index 5d1623429c0..39c50f628c2 100644
--- a/spec/frontend/ide/lib/common/model_spec.js
+++ b/spec/frontend/ide/lib/common/model_spec.js
@@ -149,7 +149,6 @@ describe('Multi-file editor library model', () => {
model.updateOptions({ insertSpaces: true, someOption: 'some value' });
expect(model.options).toEqual({
- endOfLine: 0,
insertFinalNewline: true,
insertSpaces: true,
someOption: 'some value',
@@ -181,16 +180,12 @@ describe('Multi-file editor library model', () => {
describe('applyCustomOptions', () => {
it.each`
option | value | contentBefore | contentAfter
- ${'endOfLine'} | ${0} | ${'hello\nworld\n'} | ${'hello\nworld\n'}
- ${'endOfLine'} | ${0} | ${'hello\r\nworld\r\n'} | ${'hello\nworld\n'}
- ${'endOfLine'} | ${1} | ${'hello\nworld\n'} | ${'hello\r\nworld\r\n'}
- ${'endOfLine'} | ${1} | ${'hello\r\nworld\r\n'} | ${'hello\r\nworld\r\n'}
${'insertFinalNewline'} | ${true} | ${'hello\nworld'} | ${'hello\nworld\n'}
${'insertFinalNewline'} | ${true} | ${'hello\nworld\n'} | ${'hello\nworld\n'}
${'insertFinalNewline'} | ${false} | ${'hello\nworld'} | ${'hello\nworld'}
${'trimTrailingWhitespace'} | ${true} | ${'hello \t\nworld \t\n'} | ${'hello\nworld\n'}
- ${'trimTrailingWhitespace'} | ${true} | ${'hello \t\r\nworld \t\r\n'} | ${'hello\nworld\n'}
- ${'trimTrailingWhitespace'} | ${false} | ${'hello \t\r\nworld \t\r\n'} | ${'hello \t\nworld \t\n'}
+ ${'trimTrailingWhitespace'} | ${true} | ${'hello \t\r\nworld \t\r\n'} | ${'hello\r\nworld\r\n'}
+ ${'trimTrailingWhitespace'} | ${false} | ${'hello \t\r\nworld \t\r\n'} | ${'hello \t\r\nworld \t\r\n'}
`(
'correctly applies custom option $option=$value to content',
({ option, value, contentBefore, contentAfter }) => {
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js
new file mode 100644
index 00000000000..4b4e96f3b41
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/get_base_config_spec.js
@@ -0,0 +1,22 @@
+import { getBaseConfig } from '~/ide/lib/gitlab_web_ide/get_base_config';
+import { TEST_HOST } from 'helpers/test_constants';
+
+const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/gitlab-web-ide/public/path';
+const TEST_GITLAB_URL = 'https://gdk.test/';
+
+describe('~/ide/lib/gitlab_web_ide/get_base_config', () => {
+ it('returns base properties for @gitlab/web-ide config', () => {
+ // why: add trailing "/" to test that it gets removed
+ process.env.GITLAB_WEB_IDE_PUBLIC_PATH = `${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}/`;
+ window.gon.gitlab_url = TEST_GITLAB_URL;
+
+ // act
+ const actual = getBaseConfig();
+
+ // asset
+ expect(actual).toEqual({
+ baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
+ gitlabUrl: TEST_GITLAB_URL,
+ });
+ });
+});
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js
new file mode 100644
index 00000000000..35cf41b31f5
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/setup_root_element_spec.js
@@ -0,0 +1,32 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { setupRootElement } from '~/ide/lib/gitlab_web_ide/setup_root_element';
+
+describe('~/ide/lib/gitlab_web_ide/setup_root_element', () => {
+ beforeEach(() => {
+ setHTMLFixture(`
+ <div id="ide-test-root" class="js-not-a-real-class">
+ <span>We are loading lots of stuff...</span>
+ </div>
+ `);
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ const findIDERoot = () => document.getElementById('ide-test-root');
+
+ it('has no children, has original ID, and classes', () => {
+ const result = setupRootElement(findIDERoot());
+
+ // why: Assert that the return element matches the new one found in the dom
+ // (implying a el.replaceWith...)
+ expect(result).toBe(findIDERoot());
+ expect(result).toMatchInlineSnapshot(`
+ <div
+ class="gl--flex-center gl-relative gl-h-full"
+ id="ide-test-root"
+ />
+ `);
+ });
+});
diff --git a/spec/frontend/ide/remote/index_spec.js b/spec/frontend/ide/remote/index_spec.js
new file mode 100644
index 00000000000..0f23b0a4e45
--- /dev/null
+++ b/spec/frontend/ide/remote/index_spec.js
@@ -0,0 +1,91 @@
+import { startRemote } from '@gitlab/web-ide';
+import { getBaseConfig, setupRootElement } from '~/ide/lib/gitlab_web_ide';
+import { mountRemoteIDE } from '~/ide/remote';
+import { TEST_HOST } from 'helpers/test_constants';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+
+jest.mock('@gitlab/web-ide');
+jest.mock('~/ide/lib/gitlab_web_ide');
+
+const TEST_DATA = {
+ remoteHost: 'example.com:3443',
+ remotePath: 'test/path/gitlab',
+ cspNonce: 'just7some8noncense',
+ connectionToken: 'connectAtoken',
+ returnUrl: 'https://example.com/return',
+};
+
+const TEST_BASE_CONFIG = {
+ gitlabUrl: '/test/gitlab',
+};
+
+const TEST_RETURN_URL_SAME_ORIGIN = `${TEST_HOST}/foo/example`;
+
+describe('~/ide/remote/index', () => {
+ useMockLocationHelper();
+ const originalHref = window.location.href;
+
+ let el;
+ let rootEl;
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ Object.entries(TEST_DATA).forEach(([key, value]) => {
+ el.dataset[key] = value;
+ });
+
+ // Stub setupRootElement so we can assert on return element
+ rootEl = document.createElement('div');
+ setupRootElement.mockReturnValue(rootEl);
+
+ // Stub getBaseConfig so we can assert
+ getBaseConfig.mockReturnValue(TEST_BASE_CONFIG);
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ mountRemoteIDE(el);
+ });
+
+ it('calls startRemote', () => {
+ expect(startRemote).toHaveBeenCalledWith(rootEl, {
+ ...TEST_BASE_CONFIG,
+ nonce: TEST_DATA.cspNonce,
+ connectionToken: TEST_DATA.connectionToken,
+ remoteAuthority: `/${TEST_DATA.remoteHost}`,
+ hostPath: `/${TEST_DATA.remotePath}`,
+ handleError: expect.any(Function),
+ handleClose: expect.any(Function),
+ });
+ });
+ });
+
+ describe.each`
+ returnUrl | fnName | reloadExpectation | hrefExpectation
+ ${TEST_DATA.returnUrl} | ${'handleError'} | ${1} | ${originalHref}
+ ${TEST_DATA.returnUrl} | ${'handleClose'} | ${1} | ${originalHref}
+ ${TEST_RETURN_URL_SAME_ORIGIN} | ${'handleClose'} | ${0} | ${TEST_RETURN_URL_SAME_ORIGIN}
+ ${TEST_RETURN_URL_SAME_ORIGIN} | ${'handleError'} | ${0} | ${TEST_RETURN_URL_SAME_ORIGIN}
+ ${''} | ${'handleClose'} | ${1} | ${originalHref}
+ `(
+ 'with returnUrl=$returnUrl and fn=$fnName',
+ ({ returnUrl, fnName, reloadExpectation, hrefExpectation }) => {
+ beforeEach(() => {
+ el.dataset.returnUrl = returnUrl;
+
+ mountRemoteIDE(el);
+ });
+
+ it('changes location', () => {
+ expect(window.location.reload).not.toHaveBeenCalled();
+
+ const [, config] = startRemote.mock.calls[0];
+
+ config[fnName]();
+
+ expect(window.location.reload).toHaveBeenCalledTimes(reloadExpectation);
+ expect(window.location.href).toBe(hrefExpectation);
+ });
+ },
+ );
+});
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index 0fab828dfb3..5847e8e1518 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -6,7 +6,7 @@ import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.
import services from '~/ide/services';
import { query, mutate } from '~/ide/services/gql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
-import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
+import ciConfig from '~/ci/pipeline_editor/graphql/queries/ci_config.query.graphql';
import { projectData } from '../mock_data';
jest.mock('~/api');
diff --git a/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js b/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
index fc00bd075e7..8d21088bcaf 100644
--- a/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/actions/checks_spec.js
@@ -10,7 +10,7 @@ import {
import * as messages from '~/ide/stores/modules/terminal/messages';
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
const TEST_PROJECT_PATH = 'lorem/root';
const TEST_BRANCH_ID = 'main';
@@ -78,7 +78,7 @@ describe('IDE store terminal check actions', () => {
describe('receiveConfigCheckError', () => {
it('handles error response', () => {
- const status = httpStatus.UNPROCESSABLE_ENTITY;
+ const status = HTTP_STATUS_UNPROCESSABLE_ENTITY;
const payload = { response: { status } };
return testAction(
diff --git a/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js b/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
index f48797415df..df365442c67 100644
--- a/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/actions/session_controls_spec.js
@@ -6,7 +6,7 @@ import { STARTING, PENDING, STOPPING, STOPPED } from '~/ide/stores/modules/termi
import * as messages from '~/ide/stores/modules/terminal/messages';
import * as mutationTypes from '~/ide/stores/modules/terminal/mutation_types';
import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
jest.mock('~/flash');
@@ -285,7 +285,7 @@ describe('IDE store terminal session controls actions', () => {
);
});
- [httpStatus.NOT_FOUND, httpStatus.UNPROCESSABLE_ENTITY].forEach((status) => {
+ [httpStatus.NOT_FOUND, HTTP_STATUS_UNPROCESSABLE_ENTITY].forEach((status) => {
it(`dispatches request and startSession on ${status}`, () => {
mock
.onPost(state.session.retryPath, { branch: rootState.currentBranchId, format: 'json' })
diff --git a/spec/frontend/ide/stores/modules/terminal/messages_spec.js b/spec/frontend/ide/stores/modules/terminal/messages_spec.js
index e8f375a70b5..2a802d6b4af 100644
--- a/spec/frontend/ide/stores/modules/terminal/messages_spec.js
+++ b/spec/frontend/ide/stores/modules/terminal/messages_spec.js
@@ -1,7 +1,7 @@
import { escape } from 'lodash';
import { TEST_HOST } from 'spec/test_constants';
import * as messages from '~/ide/stores/modules/terminal/messages';
-import httpStatus from '~/lib/utils/http_status';
+import httpStatus, { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import { sprintf } from '~/locale';
const TEST_HELP_URL = `${TEST_HOST}/help`;
@@ -9,7 +9,7 @@ const TEST_HELP_URL = `${TEST_HOST}/help`;
describe('IDE store terminal messages', () => {
describe('configCheckError', () => {
it('returns job error, with status UNPROCESSABLE_ENTITY', () => {
- const result = messages.configCheckError(httpStatus.UNPROCESSABLE_ENTITY, TEST_HELP_URL);
+ const result = messages.configCheckError(HTTP_STATUS_UNPROCESSABLE_ENTITY, TEST_HELP_URL);
expect(result).toBe(
sprintf(