{{ __('No changes') }}
-{{ __('Edit files in the editor and commit changes here') }}
-{{ __('Edit files in the editor and commit changes here') }}
-{{ overviewText }}
-- {{ emptyStateText }} -
-- {{ - __( - "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes.", - ) - }} -
-- {{ - __( - "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.", - ) - }} -
----
-- - - --
{{ __('Unable to create pipeline') }}
-{{ latestPipeline.yamlError }}
-{{ __('Run tests against your code live using the Web Terminal') }}
-
-
',
- codeEnd: '
',
- },
- false,
- );
- }
- if (status === HTTP_STATUS_FORBIDDEN) {
- return ERROR_PERMISSION;
- }
-
- return UNEXPECTED_ERROR_CONFIG;
-};
-
-export const runnersCheckEmpty = (helpUrl) =>
- sprintf(
- EMPTY_RUNNERS,
- {
- helpStart: ``,
- helpEnd: '',
- },
- false,
- );
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js b/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js
deleted file mode 100644
index b6a6f28abfa..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export const SET_VISIBLE = 'SET_VISIBLE';
-export const HIDE_SPLASH = 'HIDE_SPLASH';
-export const SET_PATHS = 'SET_PATHS';
-
-export const REQUEST_CHECK = 'REQUEST_CHECK';
-export const RECEIVE_CHECK_SUCCESS = 'RECEIVE_CHECK_SUCCESS';
-export const RECEIVE_CHECK_ERROR = 'RECEIVE_CHECK_ERROR';
-
-export const SET_SESSION = 'SET_SESSION';
-export const SET_SESSION_STATUS = 'SET_SESSION_STATUS';
-export const SET_SESSION_STATUS_INTERVAL = 'SET_SESSION_STATUS_INTERVAL';
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/mutations.js b/app/assets/javascripts/ide/stores/modules/terminal/mutations.js
deleted file mode 100644
index 8adde8f6b4e..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal/mutations.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import * as types from './mutation_types';
-
-export default {
- [types.SET_VISIBLE](state, isVisible) {
- Object.assign(state, {
- isVisible,
- });
- },
- [types.HIDE_SPLASH](state) {
- Object.assign(state, {
- isShowSplash: false,
- });
- },
- [types.SET_PATHS](state, paths) {
- Object.assign(state, {
- paths,
- });
- },
- [types.REQUEST_CHECK](state, type) {
- Object.assign(state.checks, {
- [type]: {
- isLoading: true,
- },
- });
- },
- [types.RECEIVE_CHECK_ERROR](state, { type, message }) {
- Object.assign(state.checks, {
- [type]: {
- isLoading: false,
- isValid: false,
- message,
- },
- });
- },
- [types.RECEIVE_CHECK_SUCCESS](state, type) {
- Object.assign(state.checks, {
- [type]: {
- isLoading: false,
- isValid: true,
- message: null,
- },
- });
- },
- [types.SET_SESSION](state, session) {
- Object.assign(state, {
- session,
- });
- },
- [types.SET_SESSION_STATUS](state, status) {
- const session = {
- ...state.session,
- status,
- };
-
- Object.assign(state, {
- session,
- });
- },
- [types.SET_SESSION_STATUS_INTERVAL](state, sessionStatusInterval) {
- Object.assign(state, {
- sessionStatusInterval,
- });
- },
-};
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/state.js b/app/assets/javascripts/ide/stores/modules/terminal/state.js
deleted file mode 100644
index f35a10ed2fe..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal/state.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { CHECK_CONFIG, CHECK_RUNNERS } from './constants';
-
-export default () => ({
- checks: {
- [CHECK_CONFIG]: { isLoading: true },
- [CHECK_RUNNERS]: { isLoading: true },
- },
- isVisible: false,
- isShowSplash: true,
- paths: {},
- session: null,
- sessionStatusInterval: 0,
-});
diff --git a/app/assets/javascripts/ide/stores/modules/terminal/utils.js b/app/assets/javascripts/ide/stores/modules/terminal/utils.js
deleted file mode 100644
index 1f4bca9f50a..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal/utils.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { STARTING, PENDING, RUNNING } from './constants';
-
-export const isStartingStatus = (status) => status === STARTING || status === PENDING;
-export const isRunningStatus = (status) => status === RUNNING;
-export const isEndingStatus = (status) => !isStartingStatus(status) && !isRunningStatus(status);
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js
deleted file mode 100644
index a2cb0666a99..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import mirror, { canConnect } from '../../../lib/mirror';
-import * as types from './mutation_types';
-
-export const upload = ({ rootState, commit }) => {
- commit(types.START_LOADING);
-
- return mirror
- .upload(rootState)
- .then(() => {
- commit(types.SET_SUCCESS);
- })
- .catch((err) => {
- commit(types.SET_ERROR, err);
- });
-};
-
-export const stop = ({ commit }) => {
- mirror.disconnect();
-
- commit(types.STOP);
-};
-
-export const start = ({ rootState, commit }) => {
- const { session } = rootState.terminal;
- const path = session && session.proxyWebsocketPath;
- if (!path || !canConnect(session)) {
- return Promise.reject();
- }
-
- commit(types.START_LOADING);
-
- return mirror
- .connect(path)
- .then(() => {
- commit(types.SET_SUCCESS);
- })
- .catch((err) => {
- commit(types.SET_ERROR, err);
- throw err;
- });
-};
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js
deleted file mode 100644
index a0685293839..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as actions from './actions';
-import mutations from './mutations';
-import state from './state';
-
-export default () => ({
- namespaced: true,
- actions,
- mutations,
- state: state(),
-});
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js
deleted file mode 100644
index e50e1a1406b..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { __ } from '~/locale';
-
-export const MSG_TERMINAL_SYNC_CONNECTING = __('Connecting to terminal sync service');
-export const MSG_TERMINAL_SYNC_UPLOADING = __('Uploading changes to terminal');
-export const MSG_TERMINAL_SYNC_RUNNING = __('Terminal sync service is running');
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js
deleted file mode 100644
index ec809540c18..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export const START_LOADING = 'START_LOADING';
-export const SET_ERROR = 'SET_ERROR';
-export const SET_SUCCESS = 'SET_SUCCESS';
-export const STOP = 'STOP';
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js
deleted file mode 100644
index 70ed137776a..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import * as types from './mutation_types';
-
-export default {
- [types.START_LOADING](state) {
- state.isLoading = true;
- state.isError = false;
- },
- [types.SET_ERROR](state, { message }) {
- state.isLoading = false;
- state.isError = true;
- state.message = message;
- },
- [types.SET_SUCCESS](state) {
- state.isLoading = false;
- state.isError = false;
- state.isStarted = true;
- },
- [types.STOP](state) {
- state.isLoading = false;
- state.isStarted = false;
- },
-};
diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js
deleted file mode 100644
index 7ec3e38f675..00000000000
--- a/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default () => ({
- isLoading: false,
- isStarted: false,
- isError: false,
- message: '',
-});
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
deleted file mode 100644
index ae6588f948f..00000000000
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ /dev/null
@@ -1,73 +0,0 @@
-export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
-export const TOGGLE_LOADING = 'TOGGLE_LOADING';
-export const SET_LAST_COMMIT_MSG = 'SET_LAST_COMMIT_MSG';
-export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS';
-export const SET_EMPTY_STATE_SVGS = 'SET_EMPTY_STATE_SVGS';
-export const SET_LINKS = 'SET_LINKS';
-
-// Project Mutation Types
-export const SET_PROJECT = 'SET_PROJECT';
-export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT';
-export const UPDATE_PROJECT = 'UPDATE_PROJECT';
-export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
-
-// Merge request mutation types
-export const SET_MERGE_REQUEST = 'SET_MERGE_REQUEST';
-export const SET_CURRENT_MERGE_REQUEST = 'SET_CURRENT_MERGE_REQUEST';
-export const SET_MERGE_REQUEST_CHANGES = 'SET_MERGE_REQUEST_CHANGES';
-export const SET_MERGE_REQUEST_VERSIONS = 'SET_MERGE_REQUEST_VERSIONS';
-
-// Branch Mutation Types
-export const SET_BRANCH = 'SET_BRANCH';
-export const SET_BRANCH_COMMIT = 'SET_BRANCH_COMMIT';
-export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE';
-export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN';
-
-// Tree mutation types
-export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA';
-export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN';
-export const SET_TREE_OPEN = 'SET_TREE_OPEN';
-export const CREATE_TREE = 'CREATE_TREE';
-export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES';
-
-// File mutation types
-export const SET_FILE_DATA = 'SET_FILE_DATA';
-export const TOGGLE_FILE_OPEN = 'TOGGLE_FILE_OPEN';
-export const SET_FILE_ACTIVE = 'SET_FILE_ACTIVE';
-export const SET_FILE_RAW_DATA = 'SET_FILE_RAW_DATA';
-export const SET_FILE_BASE_RAW_DATA = 'SET_FILE_BASE_RAW_DATA';
-export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
-export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES';
-export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED';
-export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED';
-export const TOGGLE_FILE_CHANGED = 'TOGGLE_FILE_CHANGED';
-export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH';
-export const SET_ENTRIES = 'SET_ENTRIES';
-export const CREATE_TMP_ENTRY = 'CREATE_TMP_ENTRY';
-export const SET_FILE_MERGE_REQUEST_CHANGE = 'SET_FILE_MERGE_REQUEST_CHANGE';
-export const UPDATE_VIEWER = 'UPDATE_VIEWER';
-export const UPDATE_DELAY_VIEWER_CHANGE = 'UPDATE_DELAY_VIEWER_CHANGE';
-
-export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES';
-export const STAGE_CHANGE = 'STAGE_CHANGE';
-export const UNSTAGE_CHANGE = 'UNSTAGE_CHANGE';
-export const REMOVE_FILE_FROM_STAGED_AND_CHANGED = 'REMOVE_FILE_FROM_STAGED_AND_CHANGED';
-
-export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
-export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
-export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
-
-export const UPDATE_ACTIVITY_BAR_VIEW = 'UPDATE_ACTIVITY_BAR_VIEW';
-export const UPDATE_TEMP_FLAG = 'UPDATE_TEMP_FLAG';
-export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER';
-
-export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
-export const RESET_OPEN_FILES = 'RESET_OPEN_FILES';
-
-export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
-
-export const DELETE_ENTRY = 'DELETE_ENTRY';
-export const RENAME_ENTRY = 'RENAME_ENTRY';
-export const REVERT_RENAME_ENTRY = 'REVERT_RENAME_ENTRY';
-
-export const RESTORE_TREE = 'RESTORE_TREE';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
deleted file mode 100644
index 10eaa270967..00000000000
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ /dev/null
@@ -1,250 +0,0 @@
-import * as types from './mutation_types';
-import branchMutations from './mutations/branch';
-import fileMutations from './mutations/file';
-import mergeRequestMutation from './mutations/merge_request';
-import projectMutations from './mutations/project';
-import treeMutations from './mutations/tree';
-import {
- sortTree,
- swapInParentTreeWithSorting,
- updateFileCollections,
- removeFromParentTree,
- pathsAreEqual,
-} from './utils';
-
-export default {
- [types.SET_INITIAL_DATA](state, data) {
- Object.assign(state, data);
- },
- [types.TOGGLE_LOADING](state, { entry, forceValue = undefined }) {
- if (entry.path) {
- Object.assign(state.entries[entry.path], {
- loading: forceValue !== undefined ? forceValue : !state.entries[entry.path].loading,
- });
- } else {
- Object.assign(entry, {
- loading: forceValue !== undefined ? forceValue : !entry.loading,
- });
- }
- },
- [types.SET_RESIZING_STATUS](state, resizing) {
- Object.assign(state, {
- panelResizing: resizing,
- });
- },
- [types.SET_LAST_COMMIT_MSG](state, lastCommitMsg) {
- Object.assign(state, {
- lastCommitMsg,
- });
- },
- [types.CLEAR_STAGED_CHANGES](state) {
- Object.assign(state, {
- stagedFiles: [],
- });
- },
- [types.SET_ENTRIES](state, entries) {
- Object.assign(state, {
- entries,
- });
- },
- [types.CREATE_TMP_ENTRY](state, { data }) {
- Object.keys(data.entries).reduce((acc, key) => {
- const entry = data.entries[key];
- const foundEntry = state.entries[key];
-
- // NOTE: We can't clone `entry` in any of the below assignments because
- // we need `state.entries` and the `entry.tree` to reference the same object.
- if (!foundEntry || foundEntry.deleted) {
- Object.assign(state.entries, {
- [key]: entry,
- });
- } else {
- const tree = entry.tree.filter(
- (f) => foundEntry.tree.find((e) => e.path === f.path) === undefined,
- );
- Object.assign(foundEntry, {
- tree: sortTree(foundEntry.tree.concat(tree)),
- });
- }
-
- return acc.concat(key);
- }, []);
-
- const currentTree = state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
- const foundEntry = currentTree.tree.find((e) => e.path === data.treeList[0].path);
-
- if (!foundEntry) {
- Object.assign(currentTree, {
- tree: sortTree(currentTree.tree.concat(data.treeList)),
- });
- }
- },
- [types.UPDATE_TEMP_FLAG](state, { path, tempFile }) {
- Object.assign(state.entries[path], {
- tempFile,
- changed: tempFile,
- });
- },
- [types.UPDATE_VIEWER](state, viewer) {
- Object.assign(state, {
- viewer,
- });
- },
- [types.UPDATE_DELAY_VIEWER_CHANGE](state, delayViewerUpdated) {
- Object.assign(state, {
- delayViewerUpdated,
- });
- },
- [types.UPDATE_ACTIVITY_BAR_VIEW](state, currentActivityView) {
- Object.assign(state, {
- currentActivityView,
- });
- },
- [types.SET_EMPTY_STATE_SVGS](
- state,
- {
- emptyStateSvgPath,
- noChangesStateSvgPath,
- committedStateSvgPath,
- pipelinesEmptyStateSvgPath,
- switchEditorSvgPath,
- },
- ) {
- Object.assign(state, {
- emptyStateSvgPath,
- noChangesStateSvgPath,
- committedStateSvgPath,
- pipelinesEmptyStateSvgPath,
- switchEditorSvgPath,
- });
- },
- [types.TOGGLE_FILE_FINDER](state, fileFindVisible) {
- Object.assign(state, {
- fileFindVisible,
- });
- },
- [types.UPDATE_FILE_AFTER_COMMIT](state, { file, lastCommit }) {
- const changedFile = state.changedFiles.find((f) => f.path === file.path);
- const { prevPath } = file;
-
- Object.assign(state.entries[file.path], {
- raw: file.content,
- changed: Boolean(changedFile),
- staged: false,
- lastCommitSha: lastCommit.commit.id,
-
- prevId: undefined,
- prevPath: undefined,
- prevName: undefined,
- prevKey: undefined,
- prevParentPath: undefined,
- });
-
- if (prevPath) {
- // Update URLs after file has moved
- const regex = new RegExp(`${prevPath}$`);
-
- Object.assign(state.entries[file.path], {
- rawPath: file.rawPath.replace(regex, file.path),
- });
- }
- },
- [types.SET_LINKS](state, links) {
- Object.assign(state, { links });
- },
- [types.CLEAR_PROJECTS](state) {
- Object.assign(state, { projects: {}, trees: {} });
- },
- [types.RESET_OPEN_FILES](state) {
- Object.assign(state, { openFiles: [] });
- },
- [types.SET_ERROR_MESSAGE](state, errorMessage) {
- Object.assign(state, { errorMessage });
- },
- [types.DELETE_ENTRY](state, path) {
- const entry = state.entries[path];
- const { tempFile = false } = entry;
- const parent = entry.parentPath
- ? state.entries[entry.parentPath]
- : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-
- entry.deleted = true;
-
- if (parent) {
- parent.tree = parent.tree.filter((f) => f.path !== entry.path);
- }
-
- if (entry.type === 'blob') {
- if (tempFile) {
- // Since we only support one list of file changes, it's safe to just remove from both
- // changed and staged. Otherwise, we'd need to somehow evaluate the difference between
- // changed and HEAD.
- // https://gitlab.com/gitlab-org/create-stage/-/issues/12669
- state.changedFiles = state.changedFiles.filter((f) => f.path !== path);
- state.stagedFiles = state.stagedFiles.filter((f) => f.path !== path);
- } else {
- state.changedFiles = state.changedFiles.concat(entry);
- }
- }
- },
- [types.RENAME_ENTRY](state, { path, name, parentPath }) {
- const oldEntry = state.entries[path];
- const newPath = parentPath ? `${parentPath}/${name}` : name;
- const isRevert = newPath === oldEntry.prevPath;
- const newKey = oldEntry.key.replace(new RegExp(oldEntry.path, 'g'), newPath);
-
- const baseProps = {
- ...oldEntry,
- name,
- id: newPath,
- path: newPath,
- key: newKey,
- parentPath: parentPath || '',
- };
-
- const prevProps =
- oldEntry.tempFile || isRevert
- ? {
- prevId: undefined,
- prevPath: undefined,
- prevName: undefined,
- prevKey: undefined,
- prevParentPath: undefined,
- }
- : {
- prevId: oldEntry.prevId || oldEntry.id,
- prevPath: oldEntry.prevPath || oldEntry.path,
- prevName: oldEntry.prevName || oldEntry.name,
- prevKey: oldEntry.prevKey || oldEntry.key,
- prevParentPath: oldEntry.prevParentPath || oldEntry.parentPath,
- };
-
- state.entries = {
- ...state.entries,
- [newPath]: {
- ...baseProps,
- ...prevProps,
- },
- };
-
- if (pathsAreEqual(oldEntry.parentPath, parentPath)) {
- swapInParentTreeWithSorting(state, oldEntry.key, newPath, parentPath);
- } else {
- removeFromParentTree(state, oldEntry.key, oldEntry.parentPath);
- swapInParentTreeWithSorting(state, oldEntry.key, newPath, parentPath);
- }
-
- if (oldEntry.type === 'blob') {
- updateFileCollections(state, oldEntry.key, newPath);
- }
- const stateCopy = { ...state.entries };
- delete stateCopy[oldEntry.path];
- state.entries = stateCopy;
- },
-
- ...projectMutations,
- ...mergeRequestMutation,
- ...fileMutations,
- ...treeMutations,
- ...branchMutations,
-};
diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js
deleted file mode 100644
index 6afd8de2aa4..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/branch.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import * as types from '../mutation_types';
-
-export default {
- [types.SET_CURRENT_BRANCH](state, currentBranchId) {
- Object.assign(state, {
- currentBranchId,
- });
- },
- [types.SET_BRANCH](state, { projectPath, branchName, branch }) {
- Object.assign(state.projects[projectPath], {
- branches: {
- [branchName]: {
- ...branch,
- treeId: `${projectPath}/${branchName}`,
- active: true,
- workingReference: '',
- },
- },
- });
- },
- [types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) {
- if (!state.projects[projectId].branches[branchId]) {
- Object.assign(state.projects[projectId].branches, {
- [branchId]: {},
- });
- }
-
- Object.assign(state.projects[projectId].branches[branchId], {
- workingReference: reference,
- });
- },
- [types.SET_BRANCH_COMMIT](state, { projectId, branchId, commit }) {
- Object.assign(state.projects[projectId].branches[branchId], {
- commit,
- });
- },
-};
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
deleted file mode 100644
index fa9830a7469..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ /dev/null
@@ -1,241 +0,0 @@
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import { diffModes } from '../../constants';
-import * as types from '../mutation_types';
-import { sortTree } from '../utils';
-
-export default {
- [types.SET_FILE_ACTIVE](state, { path, active }) {
- Object.assign(state.entries[path], {
- active,
- lastOpenedAt: new Date().getTime(),
- });
-
- if (active && !state.entries[path].pending) {
- Object.assign(state, {
- openFiles: state.openFiles.map((f) =>
- Object.assign(f, { active: f.pending ? false : f.active }),
- ),
- });
- }
- },
- [types.TOGGLE_FILE_OPEN](state, path) {
- const entry = state.entries[path];
-
- entry.opened = !entry.opened;
- if (entry.opened && !entry.tempFile) {
- entry.loading = true;
- }
-
- if (entry.opened) {
- Object.assign(state, {
- openFiles: state.openFiles.filter((f) => f.path !== path).concat(state.entries[path]),
- });
- } else {
- Object.assign(state, {
- openFiles: state.openFiles.filter((f) => f.key !== entry.key),
- });
- }
- },
- [types.SET_FILE_DATA](state, { data, file }) {
- const stateEntry = state.entries[file.path];
- const stagedFile = state.stagedFiles.find((f) => f.path === file.path);
- const openFile = state.openFiles.find((f) => f.path === file.path);
- const changedFile = state.changedFiles.find((f) => f.path === file.path);
-
- [stateEntry, stagedFile, openFile, changedFile].forEach((f) => {
- if (f) {
- Object.assign(
- f,
- convertObjectPropsToCamelCase(data, { dropKeys: ['path', 'name', 'raw', 'baseRaw'] }),
- {
- raw: (stateEntry && stateEntry.raw) || null,
- baseRaw: null,
- },
- );
- }
- });
- },
- [types.SET_FILE_RAW_DATA](state, { file, raw, fileDeletedAndReadded = false }) {
- const openPendingFile = state.openFiles.find(
- (f) =>
- f.path === file.path && f.pending && !(f.tempFile && !f.prevPath && !fileDeletedAndReadded),
- );
- const stagedFile = state.stagedFiles.find((f) => f.path === file.path);
-
- if (file.tempFile && file.content === '' && !fileDeletedAndReadded) {
- Object.assign(state.entries[file.path], { content: raw });
- } else if (fileDeletedAndReadded) {
- Object.assign(stagedFile, { raw });
- } else {
- Object.assign(state.entries[file.path], { raw });
- }
-
- if (!openPendingFile) return;
-
- if (!openPendingFile.tempFile) {
- openPendingFile.raw = raw;
- } else if (openPendingFile.tempFile && !fileDeletedAndReadded) {
- openPendingFile.content = raw;
- } else if (fileDeletedAndReadded) {
- Object.assign(stagedFile, { raw });
- }
- },
- [types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) {
- Object.assign(state.entries[file.path], {
- baseRaw,
- });
- },
- [types.UPDATE_FILE_CONTENT](state, { path, content }) {
- const stagedFile = state.stagedFiles.find((f) => f.path === path);
- const rawContent = stagedFile ? stagedFile.content : state.entries[path].raw;
- const changed = content !== rawContent;
-
- Object.assign(state.entries[path], {
- content,
- changed,
- });
- },
- [types.SET_FILE_MERGE_REQUEST_CHANGE](state, { file, mrChange }) {
- let diffMode = diffModes.replaced;
- if (mrChange.new_file) {
- diffMode = diffModes.new;
- } else if (mrChange.deleted_file) {
- diffMode = diffModes.deleted;
- } else if (mrChange.renamed_file) {
- diffMode = diffModes.renamed;
- }
- Object.assign(state.entries[file.path], {
- mrChange: {
- ...mrChange,
- diffMode,
- },
- });
- },
- [types.DISCARD_FILE_CHANGES](state, path) {
- const stagedFile = state.stagedFiles.find((f) => f.path === path);
- const entry = state.entries[path];
- const { deleted } = entry;
-
- Object.assign(state.entries[path], {
- content: stagedFile ? stagedFile.content : state.entries[path].raw,
- changed: false,
- deleted: false,
- });
-
- if (deleted) {
- const parent = entry.parentPath
- ? state.entries[entry.parentPath]
- : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-
- parent.tree = sortTree(parent.tree.concat(entry));
- }
- },
- [types.ADD_FILE_TO_CHANGED](state, path) {
- Object.assign(state, {
- changedFiles: state.changedFiles.concat(state.entries[path]),
- });
- },
- [types.REMOVE_FILE_FROM_CHANGED](state, path) {
- Object.assign(state, {
- changedFiles: state.changedFiles.filter((f) => f.path !== path),
- });
- },
- [types.STAGE_CHANGE](state, { path, diffInfo }) {
- const stagedFile = state.stagedFiles.find((f) => f.path === path);
-
- Object.assign(state, {
- changedFiles: state.changedFiles.filter((f) => f.path !== path),
- entries: Object.assign(state.entries, {
- [path]: Object.assign(state.entries[path], {
- staged: diffInfo.exists,
- changed: diffInfo.changed,
- tempFile: diffInfo.tempFile,
- deleted: diffInfo.deleted,
- }),
- }),
- });
-
- if (stagedFile) {
- Object.assign(stagedFile, { ...state.entries[path] });
- } else {
- state.stagedFiles = [...state.stagedFiles, { ...state.entries[path] }];
- }
-
- if (!diffInfo.exists) {
- state.stagedFiles = state.stagedFiles.filter((f) => f.path !== path);
- }
- },
- [types.UNSTAGE_CHANGE](state, { path, diffInfo }) {
- const changedFile = state.changedFiles.find((f) => f.path === path);
- const stagedFile = state.stagedFiles.find((f) => f.path === path);
-
- if (!changedFile && stagedFile) {
- Object.assign(state.entries[path], {
- ...stagedFile,
- key: state.entries[path].key,
- active: state.entries[path].active,
- opened: state.entries[path].opened,
- changed: true,
- });
-
- state.changedFiles = state.changedFiles.concat(state.entries[path]);
- }
-
- if (!diffInfo.exists) {
- state.changedFiles = state.changedFiles.filter((f) => f.path !== path);
- }
-
- Object.assign(state, {
- stagedFiles: state.stagedFiles.filter((f) => f.path !== path),
- entries: Object.assign(state.entries, {
- [path]: Object.assign(state.entries[path], {
- staged: false,
- changed: diffInfo.changed,
- tempFile: diffInfo.tempFile,
- deleted: diffInfo.deleted,
- }),
- }),
- });
- },
- [types.TOGGLE_FILE_CHANGED](state, { file, changed }) {
- Object.assign(state.entries[file.path], {
- changed,
- });
- },
- [types.ADD_PENDING_TAB](state, { file, keyPrefix = 'pending' }) {
- state.entries[file.path].opened = false;
- state.entries[file.path].active = false;
- state.entries[file.path].lastOpenedAt = new Date().getTime();
- state.openFiles.forEach((f) =>
- Object.assign(f, {
- opened: false,
- active: false,
- }),
- );
- state.openFiles = [
- {
- ...file,
- key: `${keyPrefix}-${file.key}`,
- pending: true,
- opened: true,
- active: true,
- },
- ];
- },
- [types.REMOVE_PENDING_TAB](state, file) {
- Object.assign(state, {
- openFiles: state.openFiles.filter((f) => f.key !== file.key),
- });
- },
- [types.REMOVE_FILE_FROM_STAGED_AND_CHANGED](state, file) {
- Object.assign(state, {
- changedFiles: state.changedFiles.filter((f) => f.key !== file.key),
- stagedFiles: state.stagedFiles.filter((f) => f.key !== file.key),
- });
-
- Object.assign(state.entries[file.path], {
- changed: false,
- staged: false,
- });
- },
-};
diff --git a/app/assets/javascripts/ide/stores/mutations/merge_request.js b/app/assets/javascripts/ide/stores/mutations/merge_request.js
deleted file mode 100644
index e5b5107bc93..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/merge_request.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as types from '../mutation_types';
-
-export default {
- [types.SET_CURRENT_MERGE_REQUEST](state, currentMergeRequestId) {
- Object.assign(state, {
- currentMergeRequestId,
- });
- },
- [types.SET_MERGE_REQUEST](state, { projectPath, mergeRequestId, mergeRequest }) {
- const existingMergeRequest = state.projects[projectPath].mergeRequests[mergeRequestId] || {};
-
- Object.assign(state.projects[projectPath], {
- mergeRequests: {
- [mergeRequestId]: {
- ...mergeRequest,
- active: true,
- changes: [],
- versions: [],
- baseCommitSha: null,
- ...existingMergeRequest,
- },
- },
- });
- },
- [types.SET_MERGE_REQUEST_CHANGES](state, { projectPath, mergeRequestId, changes }) {
- Object.assign(state.projects[projectPath].mergeRequests[mergeRequestId], {
- changes,
- });
- },
- [types.SET_MERGE_REQUEST_VERSIONS](state, { projectPath, mergeRequestId, versions }) {
- Object.assign(state.projects[projectPath].mergeRequests[mergeRequestId], {
- versions,
- baseCommitSha: versions.length ? versions[0].base_commit_sha : null,
- });
- },
-};
diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js
deleted file mode 100644
index 57ea2a75c6d..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/project.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as types from '../mutation_types';
-
-export default {
- [types.SET_CURRENT_PROJECT](state, currentProjectId) {
- Object.assign(state, {
- currentProjectId,
- });
- },
- [types.SET_PROJECT](state, { projectPath, project }) {
- // Add client side properties
- Object.assign(project, {
- tree: [],
- branches: {},
- mergeRequests: {},
- active: true,
- });
-
- Object.assign(state, {
- projects: { ...state.projects, [projectPath]: project },
- });
- },
- [types.TOGGLE_EMPTY_STATE](state, { projectPath, value }) {
- Object.assign(state.projects[projectPath], {
- empty_repo: value,
- });
- },
- [types.UPDATE_PROJECT](state, { projectPath, props }) {
- const project = state.projects[projectPath];
-
- if (!project || !props) {
- return;
- }
-
- Object.keys(props).reduce((acc, key) => {
- project[key] = props[key];
- return project;
- }, project);
-
- state.projects = {
- ...state.projects,
- [projectPath]: project,
- };
- },
-};
diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js
deleted file mode 100644
index c38002bd4e0..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/tree.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import * as types from '../mutation_types';
-import { sortTree, mergeTrees } from '../utils';
-
-export default {
- [types.TOGGLE_TREE_OPEN](state, path) {
- Object.assign(state.entries[path], {
- opened: !state.entries[path].opened,
- });
- },
- [types.SET_TREE_OPEN](state, path) {
- Object.assign(state.entries[path], {
- opened: true,
- });
- },
- [types.CREATE_TREE](state, { treePath }) {
- Object.assign(state, {
- trees: {
- ...state.trees,
- [treePath]: {
- tree: [],
- loading: true,
- },
- },
- });
- },
- [types.SET_DIRECTORY_DATA](state, { data, treePath }) {
- const selectedTree = state.trees[treePath];
-
- // If we opened files while loading the tree, we need to merge them
- // Otherwise, simply overwrite the tree
- const tree = !selectedTree.tree.length
- ? data
- : selectedTree.loading && mergeTrees(selectedTree.tree, data);
-
- Object.assign(selectedTree, { tree });
- },
- [types.REMOVE_ALL_CHANGES_FILES](state) {
- Object.assign(state, {
- changedFiles: [],
- });
- },
- [types.RESTORE_TREE](state, path) {
- const entry = state.entries[path];
- const parent = entry.parentPath
- ? state.entries[entry.parentPath]
- : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-
- if (!parent.tree.find((f) => f.path === path)) {
- parent.tree = sortTree(parent.tree.concat(entry));
- }
- },
-};
diff --git a/app/assets/javascripts/ide/stores/plugins/terminal.js b/app/assets/javascripts/ide/stores/plugins/terminal.js
deleted file mode 100644
index f7ed3075b0c..00000000000
--- a/app/assets/javascripts/ide/stores/plugins/terminal.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as mutationTypes from '~/ide/stores/mutation_types';
-import terminalModule from '../modules/terminal';
-
-function getPathsFromData(el) {
- return {
- webTerminalSvgPath: el.dataset.webTerminalSvgPath,
- webTerminalHelpPath: el.dataset.webTerminalHelpPath,
- webTerminalConfigHelpPath: el.dataset.webTerminalConfigHelpPath,
- webTerminalRunnersHelpPath: el.dataset.webTerminalRunnersHelpPath,
- };
-}
-
-export default function createTerminalPlugin(el) {
- return (store) => {
- store.registerModule('terminal', terminalModule());
-
- store.dispatch('terminal/setPaths', getPathsFromData(el));
-
- store.subscribe(({ type }) => {
- if (type === mutationTypes.SET_BRANCH_WORKING_REFERENCE) {
- store.dispatch('terminal/init');
- }
- });
- };
-}
diff --git a/app/assets/javascripts/ide/stores/plugins/terminal_sync.js b/app/assets/javascripts/ide/stores/plugins/terminal_sync.js
deleted file mode 100644
index 944a034fe97..00000000000
--- a/app/assets/javascripts/ide/stores/plugins/terminal_sync.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { debounce } from 'lodash';
-import { commitActionTypes } from '~/ide/constants';
-import eventHub from '~/ide/eventhub';
-import { isEndingStatus, isRunningStatus } from '../modules/terminal/utils';
-import terminalSyncModule from '../modules/terminal_sync';
-
-const UPLOAD_DEBOUNCE = 200;
-
-/**
- * Registers and controls the terminalSync vuex module based on IDE events.
- *
- * - Watches the terminal session status state to control start/stop.
- * - Listens for file change event to control upload.
- */
-export default function createMirrorPlugin() {
- return (store) => {
- store.registerModule('terminalSync', terminalSyncModule());
-
- const upload = debounce(() => {
- store.dispatch(`terminalSync/upload`);
- }, UPLOAD_DEBOUNCE);
-
- const onFilesChange = (payload) => {
- // Do nothing on a file update since we only want to trigger manually on "save".
- if (payload?.type === commitActionTypes.update) {
- return;
- }
-
- upload();
- };
-
- const stop = () => {
- store.dispatch(`terminalSync/stop`);
- eventHub.$off('ide.files.change', onFilesChange);
- };
-
- const start = () => {
- store
- .dispatch(`terminalSync/start`)
- .then(() => {
- eventHub.$on('ide.files.change', onFilesChange);
- })
- .catch(() => {
- // error is handled in store
- });
- };
-
- store.watch(
- (x) => x.terminal && x.terminal.session && x.terminal.session.status,
- (val) => {
- if (isRunningStatus(val)) {
- start();
- } else if (isEndingStatus(val)) {
- stop();
- }
- },
- );
- };
-}
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
deleted file mode 100644
index 6297231e252..00000000000
--- a/app/assets/javascripts/ide/stores/state.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { leftSidebarViews, viewerTypes } from '../constants';
-import { DEFAULT_THEME } from '../lib/themes';
-
-export default () => ({
- currentProjectId: '',
- currentBranchId: '',
- currentMergeRequestId: '',
- changedFiles: [],
- stagedFiles: [],
- endpoints: {},
- lastCommitMsg: '',
- loading: false,
- openFiles: [],
- trees: {},
- projects: {},
- panelResizing: false,
- entries: {},
- viewer: viewerTypes.edit,
- delayViewerUpdated: false,
- currentActivityView: leftSidebarViews.edit.name,
- fileFindVisible: false,
- links: {},
- errorMessage: null,
- entryModal: {
- type: '',
- path: '',
- entry: {},
- },
- renderWhitespaceInCode: false,
- editorTheme: DEFAULT_THEME,
- previewMarkdownPath: '',
- userPreferencesPath: '',
-});
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index ae51dae60db..f84104a47ed 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -1,11 +1,3 @@
-import {
- relativePathToAbsolute,
- isAbsolute,
- isRootRelative,
- isBlobUrl,
-} from '~/lib/utils/url_utility';
-import { commitActionTypes } from '../constants';
-
export const dataStructure = () => ({
id: '',
// Key will contain a mixture of ID and path
@@ -69,67 +61,6 @@ export const decorateData = (entity) => {
});
};
-export const setPageTitle = (title) => {
- document.title = title;
-};
-
-export const setPageTitleForFile = (state, file) => {
- const title = [file.path, state.currentBranchId, state.currentProjectId, 'GitLab'].join(' · ');
- setPageTitle(title);
-};
-
-export const commitActionForFile = (file) => {
- if (file.prevPath) {
- return commitActionTypes.move;
- }
- if (file.deleted) {
- return commitActionTypes.delete;
- }
- if (file.tempFile) {
- return commitActionTypes.create;
- }
-
- return commitActionTypes.update;
-};
-
-export const getCommitFiles = (stagedFiles) =>
- stagedFiles.reduce((acc, file) => {
- if (file.type === 'tree') return acc;
-
- return acc.concat({
- ...file,
- });
- }, []);
-
-export const createCommitPayload = ({
- branch,
- getters,
- newBranch,
- state,
- rootState,
- rootGetters,
-}) => ({
- branch,
- commit_message: state.commitMessage || getters.preBuiltCommitMessage,
- actions: getCommitFiles(rootState.stagedFiles).map((f) => {
- const isBlob = isBlobUrl(f.rawPath);
- const content = isBlob ? btoa(f.content) : f.content;
-
- return {
- action: commitActionForFile(f),
- file_path: f.path,
- previous_path: f.prevPath || undefined,
- content: content || undefined,
- encoding: isBlob ? 'base64' : 'text',
- last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
- };
- }),
- start_sha: newBranch ? rootGetters.lastCommit.id : undefined,
-});
-
-export const createNewMergeRequestUrl = (projectUrl, source, target) =>
- `${projectUrl}/-/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}&nav_source=webide`;
-
const sortTreesByTypeAndName = (a, b) => {
if (a.type === 'tree' && b.type === 'blob') {
return -1;
@@ -158,125 +89,3 @@ export const sortTree = (sortedTree) =>
}),
)
.sort(sortTreesByTypeAndName);
-
-export const filePathMatches = (filePath, path) => filePath.indexOf(`${path}/`) === 0;
-
-export const getChangesCountForFiles = (files, path) =>
- files.filter((f) => filePathMatches(f.path, path)).length;
-
-export const mergeTrees = (fromTree, toTree) => {
- if (!fromTree || !fromTree.length) {
- return toTree;
- }
-
- const recurseTree = (n, t) => {
- if (!n) {
- return t;
- }
- const existingTreeNode = t.find((el) => el.path === n.path);
-
- if (existingTreeNode && n.tree.length > 0) {
- existingTreeNode.opened = true;
- recurseTree(n.tree[0], existingTreeNode.tree);
- } else if (!existingTreeNode) {
- const sorted = sortTree(t.concat(n));
- t.splice(0, t.length + 1, ...sorted);
- }
- return t;
- };
-
- for (let i = 0, l = fromTree.length; i < l; i += 1) {
- recurseTree(fromTree[i], toTree);
- }
-
- return toTree;
-};
-
-// eslint-disable-next-line max-params
-export const swapInStateArray = (state, arr, key, entryPath) =>
- Object.assign(state, {
- [arr]: state[arr].map((f) => (f.key === key ? state.entries[entryPath] : f)),
- });
-
-export const getEntryOrRoot = (state, path) =>
- path ? state.entries[path] : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-
-// eslint-disable-next-line max-params
-export const swapInParentTreeWithSorting = (state, oldKey, newPath, parentPath) => {
- if (!newPath) {
- return;
- }
-
- const parent = getEntryOrRoot(state, parentPath);
-
- if (parent) {
- const tree = parent.tree
- // filter out old entry && new entry
- .filter(({ key, path }) => key !== oldKey && path !== newPath)
- // concat new entry
- .concat(state.entries[newPath]);
-
- parent.tree = sortTree(tree);
- }
-};
-
-export const removeFromParentTree = (state, oldKey, parentPath) => {
- const parent = getEntryOrRoot(state, parentPath);
-
- if (parent) {
- parent.tree = sortTree(parent.tree.filter(({ key }) => key !== oldKey));
- }
-};
-
-export const updateFileCollections = (state, key, entryPath) => {
- ['openFiles', 'changedFiles', 'stagedFiles'].forEach((fileCollection) => {
- swapInStateArray(state, fileCollection, key, entryPath);
- });
-};
-
-export const cleanTrailingSlash = (path) => path.replace(/\/$/, '');
-
-export const pathsAreEqual = (a, b) => {
- const cleanA = a ? cleanTrailingSlash(a) : '';
- const cleanB = b ? cleanTrailingSlash(b) : '';
-
- return cleanA === cleanB;
-};
-
-export function extractMarkdownImagesFromEntries(mdFile, entries) {
- /**
- * Regex to identify an image tag in markdown, like:
- *
- * 
- * 
- * 
- *
- */
- const reMdImage = /!\[([^\]]*)\]\((.*?)(?:(?="|\))"([^"]*)")?\)/gi;
- const prefix = 'gl_md_img_';
- const images = {};
-
- let content = mdFile.content || mdFile.raw;
- let i = 0;
-
- // eslint-disable-next-line max-params
- content = content.replace(reMdImage, (_, alt, path, title) => {
- const imagePath = (isRootRelative(path) ? path : relativePathToAbsolute(path, mdFile.path))
- .substr(1)
- .trim();
-
- const imageContent = entries[imagePath]?.content || entries[imagePath]?.raw;
- const imageRawPath = entries[imagePath]?.rawPath;
-
- if (!isAbsolute(path) && imageContent) {
- const src = imageRawPath;
- i += 1;
- const key = `{{${prefix}${i}}}`;
- images[key] = { alt, src, title };
- return key;
- }
- return title ? `` : ``;
- });
-
- return { content, images };
-}
diff --git a/app/assets/javascripts/ide/sync_router_and_store.js b/app/assets/javascripts/ide/sync_router_and_store.js
deleted file mode 100644
index d73ac93dc1d..00000000000
--- a/app/assets/javascripts/ide/sync_router_and_store.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * This method adds listeners to the given router and store and syncs their state with eachother
- *
- * ### Why?
- *
- * Previously the IDE had a circular dependency between a singleton router and a singleton store.
- * This causes some integration testing headaches...
- *
- * At the time, the most effecient way to break this ciruclar dependency was to:
- *
- * - Replace the router with a factory function that receives a store reference
- * - Have the store write to a certain state that can be watched by the router
- *
- * Hence... This helper function...
- */
-export const syncRouterAndStore = (router, store) => {
- const disposables = [];
-
- let currentPath = '';
-
- // sync store to router
- disposables.push(
- store.watch(
- (state) => state.router.fullPath,
- (fullPath) => {
- if (currentPath === fullPath) {
- return;
- }
-
- currentPath = fullPath;
-
- router.push(fullPath);
- },
- ),
- );
-
- // sync router to store
- disposables.push(
- router.afterEach((to) => {
- if (currentPath === to.fullPath) {
- return;
- }
-
- currentPath = to.fullPath;
- store.dispatch('router/push', currentPath, { root: true });
- }),
- );
-
- const unsync = () => {
- disposables.forEach((fn) => fn());
- };
-
- return unsync;
-};
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
index 47cac5f103a..38b622cc886 100644
--- a/app/assets/javascripts/ide/utils.js
+++ b/app/assets/javascripts/ide/utils.js
@@ -1,76 +1,5 @@
-import { flatten, isString } from 'lodash';
import { languages } from 'monaco-editor';
import { setDiagnosticsOptions as yamlDiagnosticsOptions } from 'monaco-yaml';
-import { performanceMarkAndMeasure } from '~/performance/utils';
-import { SIDE_LEFT, SIDE_RIGHT } from './constants';
-
-const toLowerCase = (x) => x.toLowerCase();
-
-const monacoLanguages = languages.getLanguages();
-const monacoExtensions = new Set(
- flatten(monacoLanguages.map((lang) => lang.extensions?.map(toLowerCase) || [])),
-);
-const monacoMimetypes = new Set(
- flatten(monacoLanguages.map((lang) => lang.mimetypes?.map(toLowerCase) || [])),
-);
-const monacoFilenames = new Set(
- flatten(monacoLanguages.map((lang) => lang.filenames?.map(toLowerCase) || [])),
-);
-
-const KNOWN_TYPES = [
- {
- isText: false,
- isMatch(mimeType) {
- return mimeType.toLowerCase().includes('image/');
- },
- },
- {
- isText: true,
- isMatch(mimeType) {
- return mimeType.toLowerCase().includes('text/');
- },
- },
- {
- isText: true,
- isMatch(mimeType, fileName) {
- const fileExtension = fileName.includes('.') ? `.${fileName.split('.').pop()}` : '';
-
- return (
- monacoExtensions.has(fileExtension.toLowerCase()) ||
- monacoMimetypes.has(mimeType.toLowerCase()) ||
- monacoFilenames.has(fileName.toLowerCase())
- );
- },
- },
-];
-
-export function isTextFile({ name, raw, binary, content, mimeType = '' }) {
- // some file objects already have a `binary` property set on them. If so, use it first
- if (typeof binary === 'boolean') return !binary;
-
- const knownType = KNOWN_TYPES.find((type) => type.isMatch(mimeType, name));
- if (knownType) return knownType.isText;
-
- // does the string contain ascii characters only (ranges from space to tilde, tabs and new lines)
- const asciiRegex = /^[ -~\t\n\r]+$/;
-
- const fileContents = raw || content;
-
- // for unknown types, determine the type by evaluating the file contents
- return isString(fileContents) && (fileContents === '' || asciiRegex.test(fileContents));
-}
-
-export const createPathWithExt = (p) => {
- const ext = p.lastIndexOf('.') >= 0 ? p.substring(p.lastIndexOf('.') + 1) : '';
-
- return `${p.substring(1, p.lastIndexOf('.') + 1 || p.length)}${ext || '.js'}`;
-};
-
-export const trimPathComponents = (path) =>
- path
- .split('/')
- .map((s) => s.trim())
- .join('/');
export function registerLanguages(def, ...defs) {
defs.forEach((lang) => registerLanguages(lang));
@@ -94,78 +23,3 @@ export function registerSchema(schema, options = {}) {
languages.json.jsonDefaults.setDiagnosticsOptions(defaultOptions);
yamlDiagnosticsOptions(defaultOptions);
}
-
-export const otherSide = (side) => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT);
-
-export function trimTrailingWhitespace(content) {
- return content.replace(/[^\S\r\n]+$/gm, '');
-}
-
-export function getPathParents(path, maxDepth = Infinity) {
- const pathComponents = path.split('/');
- const paths = [];
-
- let depth = 0;
- while (pathComponents.length && depth < maxDepth) {
- pathComponents.pop();
-
- let parentPath = pathComponents.join('/');
- if (parentPath.startsWith('/')) parentPath = parentPath.slice(1);
- if (parentPath) paths.push(parentPath);
-
- depth += 1;
- }
-
- return paths;
-}
-
-export function getPathParent(path) {
- return getPathParents(path, 1)[0];
-}
-
-export function getFileEOL(content = '') {
- return content.includes('\r\n') ? 'CRLF' : 'LF';
-}
-
-/**
- * Adds or increments the numeric suffix to a filename/branch name.
- * Retains underscore or dash before the numeric suffix if it already exists.
- *
- * Examples:
- * hello -> hello-1
- * hello-2425 -> hello-2425
- * hello.md -> hello-1.md
- * hello_2.md -> hello_3.md
- * hello_ -> hello_1
- * main-patch-22432 -> main-patch-22433
- * patch_332 -> patch_333
- *
- * @param {string} filename File name or branch name
- * @param {number} [randomize] Should randomize the numeric suffix instead of auto-incrementing?
- */
-export function addNumericSuffix(filename, randomize = false) {
- // eslint-disable-next-line max-params
- return filename.replace(/([ _-]?)(\d*)(\..+?$|$)/, (_, before, number, after) => {
- const n = randomize ? Math.random().toString().substring(2, 7).slice(-5) : Number(number) + 1;
- return `${before || '-'}${n}${after}`;
- });
-}
-
-export const measurePerformance = (
- mark,
- measureName,
- measureStart = undefined,
- measureEnd = mark,
- // eslint-disable-next-line max-params
-) => {
- performanceMarkAndMeasure({
- mark,
- measures: [
- {
- name: measureName,
- start: measureStart,
- end: measureEnd,
- },
- ],
- });
-};
diff --git a/app/assets/javascripts/pages/ide/index/index.js b/app/assets/javascripts/pages/ide/index/index.js
index 15933256e75..d192df3561e 100644
--- a/app/assets/javascripts/pages/ide/index/index.js
+++ b/app/assets/javascripts/pages/ide/index/index.js
@@ -1,4 +1,3 @@
import { startIde } from '~/ide/index';
-import extendStore from '~/ide/stores/extend';
-startIde({ extendStore });
+startIde();
diff --git a/app/assets/stylesheets/page_bundles/_ide_mixins.scss b/app/assets/stylesheets/page_bundles/_ide_mixins.scss
deleted file mode 100644
index 02d8c94e6ce..00000000000
--- a/app/assets/stylesheets/page_bundles/_ide_mixins.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-@mixin ide-trace-view {
- display: flex;
- flex-direction: column;
- height: 100%;
-
- .top-bar {
- @include build-log-bar(35px);
-
- top: 0;
- font-size: 12px;
- border-top-right-radius: $gl-border-radius-base;
-
- .controllers {
- @include build-controllers(15px, center, false, 0, inline, 0);
- }
- }
-}
diff --git a/app/assets/stylesheets/page_bundles/_ide_monaco_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_monaco_overrides.scss
deleted file mode 100644
index 5e56a540686..00000000000
--- a/app/assets/stylesheets/page_bundles/_ide_monaco_overrides.scss
+++ /dev/null
@@ -1,129 +0,0 @@
-.blob-editor-container {
- flex: 1;
- height: 0;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- .monaco-editor .lines-content .cigr {
- display: none;
- }
-
- .is-readonly .editor.original {
- .view-lines {
- cursor: default;
- }
-
- .cursors-layer {
- display: none;
- }
- }
-
- .is-deleted {
- .editor.modified {
- .margin-view-overlays,
- .lines-content,
- .decorationsOverviewRuler {
- // !important to override monaco inline styles
- display: none !important;
- }
- }
-
- .diffOverviewRuler.modified {
- // !important to override monaco inline styles
- display: none !important;
- }
- }
-
- .is-added {
- .editor.original {
- .margin-view-overlays,
- .lines-content,
- .decorationsOverviewRuler {
- // !important to override monaco inline styles
- display: none !important;
- }
- }
-
- .diffOverviewRuler.original {
- // !important to override monaco inline styles
- display: none !important;
- }
- }
-}
-
-.multi-file-editor-holder {
- height: 100%;
- min-height: 0; // firefox fix
-}
-
-// Apply theme related overrides only to the white theme and none theme
-.theme-white .blob-editor-container,
-.theme-none .blob-editor-container {
- .monaco-diff-editor {
- .editor.modified {
- box-shadow: none;
- }
-
- .diagonal-fill {
- display: none !important;
- }
-
- .diffOverview {
- background-color: $white;
- @apply gl-border-l;
- cursor: ns-resize;
- }
-
- .diffViewport {
- display: none;
- }
-
- .char-insert {
- background-color: $line-added-dark-transparent;
- }
-
- .char-delete {
- background-color: $line-removed-dark-transparent;
- }
-
- .line-numbers {
- @apply gl-text-alpha-dark-24;
- }
-
- .view-overlays {
- .line-insert {
- background-color: $line-added-transparent;
- }
-
- .line-delete {
- background-color: $line-removed-transparent;
- }
- }
-
- .margin {
- background-color: $white;
- @apply gl-border-r;
-
- .line-insert {
- border-right: 1px solid $line-added-dark;
- }
-
- .line-delete {
- border-right: 1px solid $line-removed-dark;
- }
- }
- }
-}
-
-.theme-white .multi-file-editor-holder,
-.theme-none .multi-file-editor-holder {
- &.is-readonly,
- .editor.original {
- .monaco-editor,
- .monaco-editor-background,
- .monaco-editor .inputarea.ime-input {
- background-color: $gray-10;
- }
- }
-}
diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss
deleted file mode 100644
index 05e6c6bfee3..00000000000
--- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss
+++ /dev/null
@@ -1,351 +0,0 @@
-// -------
-// Please see `app/assets/stylesheets/page_bundles/ide_themes/README.md` for a guide on contributing new themes
-// -------
-.ide {
- $bs-input-focus-border: #80bdff;
- $bs-input-focus-box-shadow: rgba(0, 123, 255, 0.25);
-
- a:not(.btn),
- .gl-button.btn-link,
- .gl-button.btn-link:hover,
- .gl-button.btn-link:focus,
- .gl-button.btn-link:active {
- color: var(--ide-link-color, $blue-600);
- }
-
- h1,
- h2,
- h3,
- h4,
- h5,
- h6,
- code,
- .md table:not(.code),
- .md,
- .md p,
- .context-header > a,
- input,
- textarea,
- .dropdown-menu li button,
- .dropdown-menu-selectable li a.is-active,
- .dropdown-menu-inner-title,
- .ide-pipeline .top-bar,
- .ide-pipeline .top-bar .controllers .controllers-buttons,
- .controllers-buttons svg,
- .nav-links li a.active,
- .gl-tabs-nav li a.gl-tab-nav-item-active,
- .md-area.is-focused {
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
-
- .badge.badge-pill {
- color: var(--ide-text-color, var(--gl-icon-color-subtle));
- background-color: var(--ide-background, $badge-bg);
- }
-
- .nav-links li:not(.md-header-toolbar) a,
- .gl-tabs-nav li a,
- .dropdown-menu-inner-content,
- .file-row .file-row-icon svg,
- .file-row:hover .file-row-icon svg {
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle));
- }
-
- .nav-links li:not(.md-header-toolbar),
- .gl-tabs-nav li {
- &:hover a,
- &.active a,
- a:hover,
- a.active {
- &,
- .badge.badge-pill {
- color: var(--ide-text-color, $black);
- border-color: var(--ide-input-border, var(--gl-border-color-strong));
- }
- }
- }
-
- .drag-handle:hover {
- background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
- }
-
- .card-header {
- background-color: var(--ide-background, var(--gl-background-color-default));
-
- .badge.badge-pill {
- background-color: var(--ide-dropdown-hover-background, $badge-bg);
- }
- }
-
- .gl-text-subtle,
- .text-secondary {
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle)) !important;
- }
-
- input[type='search']::placeholder,
- input[type='text']::placeholder,
- textarea::placeholder {
- color: var(--ide-input-border, $gl-text-color-tertiary);
- }
-
- .dropdown-input .dropdown-input-clear {
- color: var(--ide-input-border, var(--gl-control-border-color-default));
- }
-
- .ide-nav-form .input-icon {
- color: var(--ide-input-border, var(--gl-control-border-color-default));
- }
-
- code {
- background-color: var(--ide-background, var(--gl-background-color-strong));
- }
-
- .ide-pipeline .top-bar,
- .ide-terminal .top-bar {
- background-color: var(--ide-background, var(--gl-background-color-subtle));
- }
-
- .common-note-form .md-area {
- border-color: var(--ide-input-border, var(--gl-border-color-default));
- }
-
- .md table:not(.code) tr th {
- background-color: var(--ide-highlight-background, var(--gl-background-color-strong));
- }
-
- &,
- .card,
- .common-note-form .md-area {
- background-color: var(--ide-highlight-background, var(--gl-background-color-default));
- }
-
- .card,
- .card-header,
- .ide-terminal .top-bar,
- .ide-pipeline .top-bar {
- border-color: var(--ide-border-color, var(--gl-border-color-default));
- }
-
- hr {
- border-color: var(--ide-border-color, var(--gl-border-color-default));
- }
-
- .md h1,
- .md h2,
- .md blockquote,
- .md table:not(.code) tbody td,
- .md table:not(.code) tr th,
- .nav-links,
- .gl-tabs-nav,
- .common-note-form .md-area.is-focused .nav-links {
- border-color: var(--ide-border-color-alt, var(--gl-border-color-default));
- }
-
- pre {
- border-color: var(--ide-border-color-alt, var(--gl-border-color-default));
-
- code {
- background-color: var(--ide-empty-state-background, inherit);
- }
- }
-
- // highlight accents (based on navigation theme) should only apply
- // in the default white theme and "none" theme.
- &:not(.theme-white):not(.theme-none) {
- .ide-sidebar-link.active {
- color: var(--ide-highlight-accent, var(--gl-text-color-default));
- box-shadow: inset 3px 0 var(--ide-highlight-accent, var(--gl-text-color-default));
-
- &.is-right {
- box-shadow: inset -3px 0 var(--ide-highlight-accent, var(--gl-text-color-default));
- }
- }
-
- .nav-links li.active a,
- .nav-links li a.active {
- border-color: var(--ide-highlight-accent, var(--gl-text-color-default));
- }
-
- .dropdown-menu .nav-links li a.active {
- border-color: var(--ide-highlight-accent, var(--gl-text-color-default));
- }
-
- .gl-tabs-nav li a.gl-tab-nav-item-active {
- box-shadow: inset 0 -2px 0 0 var(--ide-highlight-accent, var(--gl-text-color-default));
- }
-
- // for other themes, suppress different avatar default colors for simplicity
- .avatar-container {
- &,
- .avatar {
- color: var(--ide-text-color, var(--gl-text-color-default));
- background-color: var(--ide-highlight-background, var(--gl-background-color-default));
- border-color: var(--ide-highlight-background, var(--gl-avatar-border-color-default));
- }
- }
- }
-
- input[type='text'],
- input[type='search'],
- .filtered-search-box {
- border-color: var(--ide-input-border, var(--gl-border-color-default));
- background: var(--ide-input-background, var(--gl-background-color-default)) !important;
- }
-
- input[type='text']:not([disabled]):not([readonly]):focus,
- .md-area.is-focused {
- border-color: var(--ide-input-border, $bs-input-focus-border);
- box-shadow: 0 0 0 3px var(--ide-dropdown-background, $bs-input-focus-box-shadow);
- }
-
- input[type='text'],
- input[type='search'],
- .filtered-search-box,
- textarea {
- color: var(--ide-input-color, var(--gl-text-color-default)) !important;
- }
-
- .filtered-search-box input[type='search'] {
- border-color: transparent !important;
- box-shadow: none !important;
- }
-
- .filtered-search-token .value-container,
- .filtered-search-term .value-container {
- background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
- color: var(--ide-text-color, var(--gl-text-color-default));
-
- &:hover {
- background-color: var(--ide-input-border, var(--gl-border-color-default));
- }
- }
-
- @function calc-btn-hover-padding($original-padding, $original-border: 1px) {
- @return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, #{$original-border}));
- }
-
- .btn:not(.gl-button):not(.btn-link):not([disabled]):hover {
- border-width: var(--ide-btn-hover-border-width, 1px);
- padding: calc-btn-hover-padding(6px) calc-btn-hover-padding(10px);
- }
-
- .btn:not(.gl-button):not([disabled]).btn-sm:hover {
- padding: calc-btn-hover-padding(4px) calc-btn-hover-padding(10px);
- }
-
- .btn:not(.gl-button):not([disabled]).btn-block:hover {
- padding: calc-btn-hover-padding(6px) 0;
- }
-
- .btn-default:not(.gl-button),
- .dropdown,
- .dropdown-menu-toggle {
- color: var(--ide-input-color, var(--gl-text-color-default)) !important;
- border-color: var(--ide-btn-default-border, var(--gl-border-color-default));
- }
-
- .dropdown-menu-toggle {
- border-color: var(--ide-btn-default-border, var(--gl-border-color-strong));
- background-color: var(--ide-input-background, transparent);
-
- &:hover,
- &:focus {
- background-color: var(--ide-dropdown-btn-hover-background, var(--gl-background-color-strong)) !important;
- border-color: var(--ide-dropdown-btn-hover-border, var(--gl-border-color-strong)) !important;
- }
- }
-
- // todo: remove this block after all default buttons have been migrated to gl-button
- .btn-default:not(.gl-button) {
- background-color: var(--ide-btn-default-background, var(--gl-background-color-default)) !important;
- border-color: var(--ide-btn-default-border, var(--gl-border-color-default));
-
- &:hover,
- &:focus {
- border-color: var(--ide-btn-default-hover-border, var(--gl-border-color-default)) !important;
- background-color: var(--ide-btn-default-background, var(--gl-background-color-strong)) !important;
- }
-
- &:active,
- .active {
- border-color: var(--ide-btn-default-hover-border, var(--gl-border-color-default)) !important;
- background-color: var(--ide-btn-default-background, var(--gl-border-color-default)) !important;
- }
- }
-
- .dropdown-menu {
- color: var(--ide-text-color, var(--gl-text-color-default));
- border-color: var(--ide-background, var(--gl-border-color-default));
- background-color: var(--ide-dropdown-background, var(--gl-background-color-default));
-
- .nav-links {
- background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-default));
- border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
- }
-
- .gl-tabs-nav {
- background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-default));
- box-shadow: inset 0 -2px 0 0 var(--ide-dropdown-hover-background, var(--gl-border-color-default));
- }
-
- .divider {
- background-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
- border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
- }
-
- li > a:not(.disable-hover):hover,
- li > a:not(.disable-hover):focus,
- li button:not(.disable-hover):hover,
- li button:not(.disable-hover):focus,
- li button.is-focused {
- background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
- }
-
- .dropdown-title,
- .dropdown-input {
- border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default)) !important;
- }
-
- // todo: remove this block after all disabled buttons have been migrated to gl-button
- .btn[disabled]:not(.gl-button) {
- background-color: var(--ide-btn-default-background, var(--gl-background-color-subtle)) !important;
- border: 1px solid var(--ide-btn-disabled-border, var(--gl-border-color-default)) !important;
- color: var(--ide-btn-disabled-color, var(--gl-text-color-disabled)) !important;
- }
-
- .md table:not(.code) tbody {
- background-color: var(--ide-empty-state-background, var(--gl-background-color-default));
- }
-
- .animation-container {
- [class^='skeleton-line-'] {
- background-color: var(--ide-animation-gradient-1, var(--gl-border-color-default));
-
- &::after {
- background-image: linear-gradient(to right,
- var(--ide-animation-gradient-1, var(--gl-border-color-default)) 0%,
- var(--ide-animation-gradient-2, var(--gl-background-color-subtle)) 20%,
- var(--ide-animation-gradient-1, var(--gl-border-color-default)) 40%,
- var(--ide-animation-gradient-1, var(--gl-border-color-default)) 100%);
- }
- }
- }
-
- .idiff.addition {
- background-color: var(--ide-diff-insert, $line-added-dark);
- }
-
- .idiff.deletion {
- background-color: var(--ide-diff-remove, $line-removed-dark);
- }
-
- ~ .popover {
- box-shadow: none;
- }
-}
-
-.navbar:not(.theme-white):not(.theme-none) {
- border-bottom-color: transparent;
-}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
deleted file mode 100644
index b59865949e1..00000000000
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ /dev/null
@@ -1,1053 +0,0 @@
-@import 'framework/variables';
-@import 'framework/mixins';
-@import './ide_mixins';
-@import './ide_monaco_overrides';
-@import './ide_theme_overrides';
-
-@import './ide_themes/dark';
-@import './ide_themes/solarized-light';
-@import './ide_themes/solarized-dark';
-@import './ide_themes/monokai';
-
-// This whole file is for the legacy Web IDE
-// See: https://gitlab.com/groups/gitlab-org/-/epics/7683
-
-$search-list-icon-width: 18px;
-$ide-activity-bar-width: 60px;
-$ide-context-header-padding: 10px;
-$ide-project-avatar-end: $ide-context-header-padding + 48px;
-$ide-tree-padding: $gl-padding;
-$ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
-$ide-commit-row-height: 32px;
-$ide-commit-header-height: 48px;
-
-.web-ide-loader {
- padding-top: 1rem;
-}
-
-.commit-message {
- @include str-truncated(250px);
-}
-
-.ide-view {
- position: relative;
- margin-top: 0;
- padding-bottom: $ide-statusbar-height;
- color: var(--ide-text-color, var(--gl-text-color-default));
- min-height: 0; // firefox fix
-
- &.is-collapsed {
- .ide-file-list {
- max-width: 250px;
- }
- }
-
- .file-status-icon {
- width: 10px;
- height: 10px;
- }
-}
-
-.ide-file-list {
- display: flex;
- flex-direction: column;
- flex: 1;
- min-height: 0; // firefox fix
-}
-
-.multi-file-loading-container {
- margin-top: 10px;
- padding: 10px;
-}
-
-.multi-file-edit-pane {
- display: flex;
- flex-direction: column;
- flex: 1;
- border-left: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- border-right: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- overflow: hidden;
-}
-
-.multi-file-tabs {
- display: flex;
- background-color: var(--ide-background, $gray-10);
- box-shadow: inset 0 -1px var(--ide-border-color, var(--gl-border-color-default));
-
- > ul {
- display: flex;
- overflow-x: auto;
- }
-
- li {
- display: flex;
- align-items: center;
- padding: $grid-size $gl-padding;
- background-color: var(--ide-background-hover, $gray-50);
- border-right: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- border-bottom: 1px solid var(--ide-border-color, var(--gl-border-color-default));
-
- // stylelint-disable-next-line gitlab/no-gl-class
- &.active,
- .gl-tab-nav-item-active {
- background-color: var(--ide-highlight-background, $white);
- border-bottom-color: transparent;
- }
-
- &:not(.disabled) {
- .multi-file-tab {
- cursor: pointer;
- }
- }
-
- &.disabled {
- .multi-file-tab-close {
- cursor: default;
- }
- }
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .gl-tab-content {
- padding: 0;
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .gl-tabs-nav {
- border-width: 0;
-
- li {
- padding: 0 !important;
- background: transparent !important;
- border: 0 !important;
-
- a {
- display: flex;
- align-items: center;
- padding: $grid-size $gl-padding !important;
- box-shadow: none !important;
- font-weight: normal !important;
-
- background-color: var(--ide-background-hover, $gray-50);
- border-right: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- border-bottom: 1px solid var(--ide-border-color, var(--gl-border-color-default));
-
- // stylelint-disable-next-line gitlab/no-gl-class
- &.gl-tab-nav-item-active {
- background-color: var(--ide-highlight-background, $white);
- border-color: var(--ide-border-color, var(--gl-border-color-default));
- border-bottom-color: transparent;
- }
-
- .multi-file-tab-close svg {
- top: 0;
- }
- }
- }
- }
-}
-
-.multi-file-tab {
- @include str-truncated(141px);
-
- svg {
- vertical-align: middle;
- }
-}
-
-.multi-file-tab-close {
- width: 16px;
- height: 16px;
- padding: 0;
- margin-left: $grid-size;
- background: none;
- border: 0;
- border-radius: $gl-border-radius-base;
- color: var(--ide-text-color, $gray-900);
-
- svg {
- position: relative;
- top: -2px;
- }
-
- &:not([disabled]):hover {
- background-color: var(--ide-input-border, var(--gl-border-color-default));
- }
-
- &:not([disabled]):focus {
- background-color: var(--ide-link-color, $blue-500);
- color: $white;
- outline: 0;
-
- svg {
- fill: currentColor;
- }
- }
-}
-
-.multi-file-edit-pane-content {
- flex: 1;
- height: 0;
-}
-
-.preview-container {
- flex-grow: 1;
- position: relative;
-
- .md-previewer {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- overflow: auto;
- padding: $gl-padding;
- background-color: var(--ide-empty-state-background, transparent);
-
- .md {
- max-width: $limited-layout-width;
- }
- }
-
- .file-container {
- background-color: var(--ide-empty-state-background, $gray-50);
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-
- text-align: center;
-
- .file-content {
- padding: $gl-padding;
- max-width: 100%;
- max-height: 100%;
- }
-
- .file-info {
- font-size: $label-font-size;
- color: var(--ide-text-color, var(--gl-text-color-subtle));
- }
- }
-}
-
-.ide-status-bar {
- color: var(--ide-text-color, var(--gl-text-color-default));
- border-top: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- padding: 2px $gl-padding-8 0;
- background-color: var(--ide-footer-background, $white);
- display: flex;
- justify-content: space-between;
- height: $ide-statusbar-height;
-
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
-
- font-size: 12px;
- line-height: 22px;
-
- * {
- font-size: inherit;
- }
-
- svg {
- vertical-align: sub;
- }
-
- .ide-status-avatar {
- float: none;
- margin: 0 0 1px;
- }
-}
-
-.ide-status-list {
- > div + div {
- padding-left: $gl-padding;
- }
-}
-
-.file-content.blob-no-preview {
- a {
- margin-left: auto;
- margin-right: auto;
- }
-}
-
-.multi-file-commit-panel {
- display: flex;
- position: relative;
- padding: 0;
- background-color: var(--ide-background, $gray-10);
-
- .context-header {
- width: auto;
- margin-right: 0;
-
- > a,
- > button {
- text-decoration: none;
- padding-top: $gl-padding-8;
- padding-bottom: $gl-padding-8;
- }
- }
-
- .multi-file-commit-panel-inner {
- position: relative;
- display: flex;
- flex-direction: column;
- min-height: 100%;
- min-width: 0;
- width: 100%;
- }
-
- .multi-file-commit-panel-inner-content {
- display: flex;
- flex: 1;
- flex-direction: column;
- background-color: var(--ide-highlight-background, $white);
- border-left: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- border-top: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- border-top-left-radius: $border-radius-small;
- min-height: 0; // firefox fix
- }
-}
-
-.multi-file-commit-panel-section {
- display: flex;
- flex-direction: column;
- flex: 1;
- max-height: 100%;
- overflow: auto;
-}
-
-.ide-commit-empty-state {
- padding: 0 $gl-padding;
-}
-
-.ide-commit-empty-state-container {
- margin-top: auto;
- margin-bottom: auto;
-}
-
-.multi-file-commit-panel-header {
- height: $ide-commit-header-height;
- border-bottom: 1px solid var(--ide-border-color-alt, var(--gl-border-color-default));
- padding: 12px 0;
-}
-
-.multi-file-commit-list {
- flex: 1;
- overflow: auto;
- padding: $grid-size 0;
- min-height: 60px;
-
- &.form-text {
- margin-left: 0;
- right: 0;
- }
-}
-
-.multi-file-commit-list-path {
- display: flex;
- align-items: center;
- margin-left: -$grid-size;
- margin-right: -$grid-size;
- padding: $grid-size / 2 $grid-size;
- border-radius: $gl-border-radius-base;
- text-align: left;
- cursor: pointer;
- height: $ide-commit-row-height;
- padding-right: 0;
-
- &:hover,
- &:focus {
- background: var(--ide-background, $gray-50);
-
- outline: 0;
- }
-
- &:active {
- background: var(--ide-background, $gray-100);
- }
-
- &.is-active {
- background-color: var(--ide-background, $gray-50);
- }
-
- svg {
- min-width: 16px;
- vertical-align: middle;
- display: inline-block;
- }
-}
-
-.multi-file-commit-list-file-path {
- @include str-truncated(calc(100% - 30px));
- user-select: none;
-
- &:active {
- text-decoration: none;
- }
-}
-
-.multi-file-commit-form {
- position: relative;
- background-color: var(--ide-highlight-background, $white);
- border-left: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- transition: all 0.3s ease;
-
- > form,
- > .commit-form-compact {
- padding: $gl-padding 0;
- margin-left: $gl-padding;
- margin-right: $gl-padding;
- border-top: 1px solid var(--ide-border-color-alt, var(--gl-border-color-default));
- }
-
- .btn {
- font-size: $gl-font-size;
- }
-
- .multi-file-commit-panel-success-message {
- top: 0;
- }
-}
-
-.multi-file-commit-panel-bottom {
- position: relative;
-}
-
-.dirty-diff {
- // !important need to override monaco inline style
- width: 4px !important;
- left: 0 !important;
-
- &-modified {
- background-color: $blue-500;
- }
-
- &-added {
- background-color: $green-600;
- }
-
- &-removed {
- height: 0 !important;
- width: 0 !important;
- bottom: -2px;
- border-style: solid;
- border-width: 5px;
- border-color: transparent transparent transparent $red-500;
-
- &::before {
- content: '';
- position: absolute;
- left: 0;
- top: 0;
- width: 100px;
- height: 1px;
- background-color: rgba($red-500, 0.5);
- }
- }
-}
-
-.ide-empty-state {
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: var(--ide-empty-state-background, transparent);
-}
-
-.ide {
- overflow: hidden;
- flex: 1;
- height: calc(100vh - var(--top-bar-height))
-}
-
-.ide-commit-list-container {
- display: flex;
- flex: 1;
- flex-direction: column;
- min-height: 140px;
- margin-left: $gl-padding;
- margin-right: $gl-padding;
-
- &.is-first {
- border-bottom: 1px solid var(--ide-border-color-alt, var(--gl-border-color-default));
- }
-}
-
-.ide-commit-options {
- .is-disabled {
- .ide-option-label {
- text-decoration: line-through;
- }
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .gl-form-radio,
- .gl-form-checkbox {
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
-}
-
-.ide-sidebar-link {
- display: flex;
- align-items: center;
- justify-content: center;
- position: relative;
- height: 60px;
- width: 100%;
- padding: 0 $gl-padding;
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle));
- background-color: transparent;
- border: 0;
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
- outline: 0;
- cursor: pointer;
-
- svg {
- margin: 0 auto;
- }
-
- &:hover {
- color: var(--ide-text-color, var(--gl-text-color-default));
- background-color: var(--ide-background-hover, $gray-50);
- }
-
- &:focus {
- color: var(--ide-text-color, var(--gl-text-color-default));
- background-color: var(--ide-background-hover, $gray-100);
- }
-
- &.active {
- // extend width over border of sidebar section
- width: calc(100% + 1px);
- padding-right: $gl-padding + 1px;
- background-color: var(--ide-highlight-background, $white);
- border-top-color: var(--ide-border-color, var(--gl-border-color-default));
- border-bottom-color: var(--ide-border-color, var(--gl-border-color-default));
-
- &::after {
- content: '';
- position: absolute;
- right: -1px;
- top: 0;
- bottom: 0;
- width: 1px;
- background: var(--ide-highlight-background, $white);
- }
- }
-
- &.is-right {
- padding-right: $gl-padding;
- padding-left: $gl-padding + 1px;
-
- &::after {
- right: auto;
- left: -1px;
- }
- }
-}
-
-.ide-activity-bar {
- position: relative;
- flex: 0 0 $ide-activity-bar-width;
- z-index: 1;
-}
-
-.ide-commit-message-field {
- height: 200px;
- background-color: var(--ide-highlight-background, $white);
-
- .md-area {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .nav-links,
- .gl-tabs-nav {
- height: 30px;
- }
-
- .form-text {
- margin-top: 2px;
- color: var(--ide-link-color, $blue-500);
- cursor: pointer;
- }
-}
-
-.ide-commit-message-textarea-container {
- position: relative;
- width: 100%;
- height: 100%;
- overflow: hidden;
-
- .note-textarea {
- font-family: $monospace-font;
- }
-}
-
-.ide-commit-message-highlights-container {
- position: absolute;
- left: 0;
- top: 0;
- right: -100px;
- bottom: 0;
- padding-right: 100px;
- pointer-events: none;
- z-index: 1;
-
- .highlights {
- white-space: pre-wrap;
- word-wrap: break-word;
- color: transparent;
- }
-
- mark {
- margin-left: -1px;
- padding: 0 2px;
- border-radius: $border-radius-small;
- background-color: $orange-200;
- color: transparent;
- opacity: 0.6;
- }
-}
-
-.ide-commit-message-textarea {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- width: 100%;
- height: 100%;
- z-index: 2;
- background: transparent;
- resize: none;
-}
-
-.ide-tree-header {
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- padding: 12px 0;
- margin-left: $ide-tree-padding;
- margin-right: $ide-tree-padding;
- border-bottom: 1px solid var(--ide-border-color-alt, var(--gl-border-color-default));
-
- svg {
- color: var(--ide-text-color-secondary, var(--gl-icon-color-subtle));
-
- &:focus,
- &:hover {
- color: var(--ide-link-color, $blue-600);
- }
- }
-
- .ide-new-btn {
- margin-left: auto;
- }
-
- button {
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
-}
-
-.ide-nav-dropdown {
- width: 100%;
- margin-bottom: 12px;
-
- .dropdown-menu {
- width: 385px;
- max-height: initial;
- }
-
- .dropdown-menu-toggle {
- background-color: var(--ide-input-background, transparent);
-
- &:hover {
- background-color: var(--ide-dropdown-btn-hover-background, $gray-50);
- }
-
- svg {
- vertical-align: middle;
-
- &,
- &:hover {
- color: var(--ide-text-color-secondary, var(--gl-icon-color-subtle));
- }
- }
- }
-}
-
-.ide-tree-body {
- overflow: auto;
- padding-left: $ide-tree-padding;
- padding-right: $ide-tree-padding;
-}
-
-.commit-form-compact {
- .btn {
- margin-bottom: 8px;
- }
-
- p {
- margin-bottom: 0;
- }
-}
-
-// These are composite classes for use with Vue Transition
-// https://vuejs.org/guide/built-ins/transition
-.commit-form-slide-up-enter-active,
-.commit-form-slide-up-leave-active {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- transition: all 0.3s ease;
-}
-
-.is-full .commit-form-slide-up-enter,
-.is-compact .commit-form-slide-up-leave-to {
- transform: translateY(100%);
-}
-
-.is-full .commit-form-slide-up-enter-to,
-.is-compact .commit-form-slide-up-leave {
- transform: translateY(0);
-}
-
-.fade-enter,
-.fade-leave-to,
-.commit-form-slide-up-enter,
-.commit-form-slide-up-leave-to {
- opacity: 0;
-}
-
-.ide-review-header {
- flex-direction: column;
- align-items: flex-start;
-
- .dropdown {
- margin-left: auto;
- }
-
- a {
- color: var(--ide-link-color, $blue-600);
- }
-}
-
-.ide-review-sub-header {
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle));
-}
-
-.ide-tree-changes {
- display: flex;
- align-items: center;
- font-size: 12px;
-}
-
-.multi-file-commit-panel-success-message {
- position: absolute;
- top: 61px;
- left: 1px;
- bottom: 0;
- right: 0;
- z-index: 10;
- background-color: var(--ide-highlight-background, $white);
- overflow: auto;
- display: flex;
- flex-direction: column;
- justify-content: center;
-}
-
-.ide-review-button-holder {
- display: flex;
- width: 100%;
- align-items: center;
-}
-
-.ide-context-header {
- .avatar-container {
- flex: 0 0 auto;
- margin-right: 0;
- }
-
- .ide-sidebar-project-title {
- margin-left: $ide-tree-text-start - $ide-project-avatar-end;
- }
-}
-
-.ide-context-body {
- min-height: 0; // firefox fix
-}
-
-.ide-sidebar-project-title {
- min-width: 0;
-
- .sidebar-context-title {
- white-space: nowrap;
- display: block;
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
-}
-
-.ide-external-link {
- svg {
- display: none;
- }
-
- &:hover,
- &:focus {
- svg {
- display: inline-block;
- }
- }
-}
-
-.ide-sidebar {
- min-width: 60px;
-}
-
-.ide-pipeline {
- @include ide-trace-view();
-
- svg {
- --svg-status-bg: var(--ide-background, #{$white});
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .gl-empty-state {
- p {
- margin: $grid-size 0;
- text-align: center;
- line-height: 24px;
- }
-
- .btn,
- h4 {
- margin: 0;
- }
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- .gl-tab-content {
- color: var(--ide-text-color, var(--gl-text-color-default));
- }
-}
-
-.ide-pipeline-header {
- min-height: 55px;
- padding-left: $gl-padding;
- padding-right: $gl-padding;
-}
-
-.ide-job-item {
- display: flex;
- padding: 16px;
-
- &:not(:last-child) {
- border-bottom: 1px solid var(--ide-border-color, var(--gl-border-color-default));
- }
-
- .ci-status-icon {
- display: flex;
- justify-content: center;
- min-width: 24px;
- overflow: hidden;
- }
-}
-
-.ide-stage {
- .card-header {
- .ci-status-icon {
- display: flex;
- align-items: center;
- }
- }
-}
-
-.ide-job-header {
- min-height: 60px;
- padding: 0 $gl-padding;
-}
-
-.ide-nav-form {
- // stylelint-disable-next-line gitlab/no-gl-class
- .nav-links li,
- .gl-tabs-nav li {
- width: 50%;
- padding-left: 0;
- padding-right: 0;
-
- a {
- text-align: center;
- font-size: 14px;
- line-height: 30px;
-
- // stylelint-disable-next-line gitlab/no-gl-class
- &:not(.active),
- &:not(.gl-tab-nav-item-active) {
- background-color: var(--ide-dropdown-background, $gray-10);
- }
-
- // stylelint-disable-next-line gitlab/no-gl-class
- &.gl-tab-nav-item-active {
- font-weight: bold;
- }
- }
- }
-
- .dropdown-input {
- padding-left: $gl-padding;
- padding-right: $gl-padding;
-
- .input-icon {
- right: auto;
- left: 10px;
- top: 1rem;
- }
- }
-
- .dropdown-input-field {
- padding-left: $search-list-icon-width + $gl-padding;
- padding-top: 2px;
- padding-bottom: 2px;
- }
-
- .tokens-container {
- padding-left: $search-list-icon-width + $gl-padding;
- overflow-x: hidden;
- }
-
- .btn-link {
- padding-top: $gl-padding;
- padding-bottom: $gl-padding;
- }
-}
-
-.ide-search-list-current-icon {
- min-width: $search-list-icon-width;
-}
-
-.ide-search-list-empty {
- height: 69px;
-}
-
-.ide-merge-requests-dropdown-content {
- max-height: 470px;
-}
-
-.ide-merge-request-project-path {
- font-size: 12px;
- line-height: 16px;
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle));
-}
-
-.ide-entry-dropdown-toggle {
- padding: $gl-padding-4;
- color: var(--ide-text-color, var(--gl-text-color-default));
- background-color: var(--ide-background, $gray-50);
-
- &:hover {
- background-color: var(--ide-file-row-btn-hover-background, $gray-100);
- }
-
- &:active,
- &:focus {
- color: $gray-50;
- background-color: var(--ide-link-color, $blue-500);
- outline: 0;
- }
-}
-
-.ide-new-btn {
- display: none;
-
- .btn {
- padding: 2px 5px;
- }
-
- .dropdown.show .ide-entry-dropdown-toggle {
- color: $gray-50;
- background-color: var(--ide-link-color, $blue-500);
- }
-}
-
-.ide-file-templates {
- padding: $grid-size $gl-padding;
- background-color: var(--ide-background, $gray-10);
- border-bottom: 1px solid var(--ide-border-color, var(--gl-border-color-default));
-
- .dropdown {
- min-width: 180px;
- }
-
- .dropdown-content {
- max-height: 222px;
- }
-}
-
-.ide-commit-editor-header {
- height: 65px;
- padding: 8px 16px;
- background-color: var(--ide-background, $gray-10);
- box-shadow: inset 0 -1px var(--ide-border-color, var(--gl-border-color-default));
-}
-
-.ide-commit-list-changed-icon {
- width: $ide-commit-row-height;
- height: $ide-commit-row-height;
-}
-
-.ide-file-icon-holder {
- display: flex;
- align-items: center;
- color: var(--ide-text-color-secondary, var(--gl-text-color-subtle));
-}
-
-.file-row:active {
- background: var(--ide-background, $gray-100);
-}
-
-.file-row.is-active {
- background: var(--ide-background, $gray-50);
-}
-
-.file-row:hover,
-.file-row:focus {
- background: var(--ide-background, $gray-50);
-
- .ide-new-btn {
- display: block;
- }
-
- .folder-icon {
- fill: var(--ide-text-color-secondary, var(--gl-icon-color-subtle));
- }
-}
-
-.ide-terminal {
- @include ide-trace-view();
-
- .terminal-wrapper {
- background: $black;
- @apply gl-text-disabled;
- overflow: hidden;
- }
-
- .xterm {
- height: 100%;
- padding: $grid-size;
- }
-
- .xterm-viewport {
- overflow-y: auto;
- }
-}
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/README.md b/app/assets/stylesheets/page_bundles/ide_themes/README.md
deleted file mode 100644
index 82e89aef49b..00000000000
--- a/app/assets/stylesheets/page_bundles/ide_themes/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Web IDE Themes
-
-Web IDE currently supports 5 syntax highlighting themes based on themes from the user's profile preferences:
-
-* White
-* Dark
-* Monokai
-* Solarized Dark
-* Solarized Light
-
-Currently, the Web IDE supports the white theme by default, and the dark theme by the introduction of CSS
-variables.
-
-The Web IDE automatically adds an appropriate theme class to the `ide.vue` component based on the current syntax
-highlighting theme. Below are those theme classes, which come from the `gon.user_color_scheme` global setting:
-
-| # | Color Scheme | `gon.user_color_scheme` | Theme class |
-|---|-----------------|-------------------------|-------------------------|
-| 1 | White | `"white"` | `.theme-white` |
-| 2 | Dark | `"dark"` | `.theme-dark` |
-| 3 | Monokai | `"monokai"` | `.theme-monokai` |
-| 4 | Solarized Dark | `"solarized-dark"` | `.theme-solarized-dark` |
-| 5 | Solarized Light | `"solarized-light"` | `.theme-solarized-light` |
-| 6 | None | `"none"` | `.theme-none` |
-
-## Adding New Themes (SCSS)
-
-To add a new theme, follow the following steps:
-
-1. Pick a theme from the table above, lets say **Solarized Dark**.
-2. Create a new file in this folder called `_solarized_dark.scss`.
-3. Copy over all the CSS variables from `_dark.scss` to `_solarized_dark.scss` and assign them your own values.
- Put them under the selector `.ide.theme-solarized-dark`.
-4. Import this newly created SCSS file in `ide.scss` file in the parent directory.
-5. That's it! Raise a merge request with your newly added theme.
-
-## Modifying Monaco Themes
-
-Monaco themes are defined in Javascript and are stored in the `app/assets/javascripts/ide/lib/themes/` directory.
-To modify any syntax highlighting colors or to synchronize the theme colors with syntax highlighting colors, you
-can modify the files in that directory directly.
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss
deleted file mode 100644
index 461e054ec85..00000000000
--- a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-// -------
-// Please see `app/assets/stylesheets/page_bundles/ide_themes/README.md` for a guide on contributing new themes
-// -------
-.ide.theme-dark {
- --ide-border-color: #1d1f21;
- --ide-border-color-alt: #333;
- --ide-highlight-accent: #fff;
- --ide-text-color: #ccc;
- --ide-text-color-secondary: #ccc;
- --ide-background: #333;
- --ide-background-hover: #2d2d2d;
- --ide-highlight-background: #252526;
- --ide-link-color: #428fdc;
- --ide-footer-background: #060606;
- --ide-empty-state-background: var(--ide-border-color);
-
- --ide-input-border: #868686;
- --ide-input-background: transparent;
- --ide-input-color: #fff;
-
- --ide-btn-default-background: transparent;
- --ide-btn-default-border: #bfbfbf;
- --ide-btn-default-hover-border: #d8d8d8;
- --ide-btn-default-hover-border-width: 2px;
- --ide-btn-default-focus-box-shadow: 0 0 0 1px #bfbfbf;
-
- --ide-btn-primary-background: #1068bf;
- --ide-btn-primary-border: #428fdc;
- --ide-btn-primary-hover-border: #63a6e9;
- --ide-btn-primary-hover-border-width: 2px;
- --ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
-
- // Danger styles should be the same as default styles in dark theme
- --ide-btn-danger-secondary-background: var(--ide-btn-default-background);
- --ide-btn-danger-secondary-border: var(--ide-btn-default-border);
- --ide-btn-danger-secondary-hover-border: var(--ide-btn-default-hover-border);
- --ide-btn-danger-secondary-hover-border-width: var(--ide-btn-default-hover-border-width);
- --ide-btn-danger-secondary-focus-box-shadow: var(--ide-btn-default-focus-box-shadow);
-
- --ide-btn-disabled-background: transparent;
- --ide-btn-disabled-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border-width: 1px;
- --ide-btn-disabled-focus-box-shadow: 0 0 0 0 transparent;
- --ide-btn-disabled-color: rgba(145, 145, 145, 0.48);
-
- --ide-dropdown-background: #404040;
- --ide-dropdown-hover-background: #525252;
-
- --ide-dropdown-btn-hover-border: #{$gray-200};
- --ide-dropdown-btn-hover-background: #{$gray-900};
-
- --ide-file-row-btn-hover-background: #{$gray-800};
-
- --ide-diff-insert: rgba(155, 185, 85, 0.2);
- --ide-diff-remove: rgba(255, 0, 0, 0.2);
-
- --ide-animation-gradient-1: #{$gray-800};
- --ide-animation-gradient-2: #{$gray-700};
-}
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss b/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss
deleted file mode 100644
index 3bf0856b392..00000000000
--- a/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-// -------
-// Please see `app/assets/stylesheets/page_bundles/ide_themes/README.md` for a guide on contributing new themes
-// -------
-.ide.theme-monokai {
- --ide-border-color: #1a1a18;
- --ide-border-color-alt: #3f4237;
- --ide-highlight-accent: #fff;
- --ide-text-color: #ccc;
- --ide-text-color-secondary: #b7b7b7;
- --ide-background: #282822;
- --ide-background-hover: #2d2d2d;
- --ide-highlight-background: #1f1f1d;
- --ide-link-color: #428fdc;
- --ide-footer-background: #404338;
- --ide-empty-state-background: #1a1a18;
-
- --ide-input-border: #7d8175;
- --ide-input-background: transparent;
- --ide-input-color: #fff;
-
- --ide-btn-default-background: transparent;
- --ide-btn-default-border: #7d8175;
- --ide-btn-default-hover-border: #b5bda5;
- --ide-btn-default-hover-border-width: 2px;
- --ide-btn-default-focus-box-shadow: 0 0 0 1px #bfbfbf;
-
- --ide-btn-primary-background: #1068bf;
- --ide-btn-primary-border: #428fdc;
- --ide-btn-primary-hover-border: #63a6e9;
- --ide-btn-primary-hover-border-width: 2px;
- --ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
-
- // Danger styles should be the same as default styles in dark theme
- --ide-btn-danger-secondary-background: var(--ide-btn-default-background);
- --ide-btn-danger-secondary-border: var(--ide-btn-default-border);
- --ide-btn-danger-secondary-hover-border: var(--ide-btn-default-hover-border);
- --ide-btn-danger-secondary-hover-border-width: var(--ide-btn-default-hover-border-width);
- --ide-btn-danger-secondary-focus-box-shadow: var(--ide-btn-default-focus-box-shadow);
-
- --ide-btn-disabled-background: transparent;
- --ide-btn-disabled-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border-width: 1px;
- --ide-btn-disabled-focus-box-shadow: 0 0 0 0 transparent;
- --ide-btn-disabled-color: rgba(145, 145, 145, 0.48);
-
- --ide-dropdown-background: #36382f;
- --ide-dropdown-hover-background: #404338;
-
- --ide-dropdown-btn-hover-border: #b5bda5;
- --ide-dropdown-btn-hover-background: #3f4237;
-
- --ide-file-row-btn-hover-background: #404338;
-
- --ide-diff-insert: rgba(155, 185, 85, 0.2);
- --ide-diff-remove: rgba(255, 0, 0, 0.2);
-
- --ide-animation-gradient-1: #404338;
- --ide-animation-gradient-2: #36382f;
-}
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss
deleted file mode 100644
index d7985601e8d..00000000000
--- a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-// -------
-// Please see `app/assets/stylesheets/page_bundles/ide_themes/README.md` for a guide on contributing new themes
-// -------
-.ide.theme-solarized-dark {
- --ide-border-color: #002c38;
- --ide-border-color-alt: var(--ide-background);
- --ide-highlight-accent: #fff;
- --ide-text-color: #ddd;
- --ide-text-color-secondary: #ddd;
- --ide-background: #004152;
- --ide-background-hover: #003b4d;
- --ide-highlight-background: #003240;
- --ide-link-color: #73b9ff;
- --ide-footer-background: var(--ide-highlight-background);
- --ide-empty-state-background: var(--ide-border-color);
-
- --ide-input-border: #d8d8d8;
- --ide-input-background: transparent;
- --ide-input-color: #fff;
-
- --ide-btn-default-background: transparent;
- --ide-btn-default-border: #d8d8d8;
- --ide-btn-default-hover-border: #d8d8d8;
- --ide-btn-default-hover-border-width: 2px;
- --ide-btn-default-focus-box-shadow: 0 0 0 1px #d8d8d8;
-
- --ide-btn-primary-background: #1068bf;
- --ide-btn-primary-border: #428fdc;
- --ide-btn-primary-hover-border: #63a6e9;
- --ide-btn-primary-hover-border-width: 2px;
- --ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
-
- // Danger styles should be the same as default styles in dark theme
- --ide-btn-danger-secondary-background: var(--ide-btn-default-background);
- --ide-btn-danger-secondary-border: var(--ide-btn-default-border);
- --ide-btn-danger-secondary-hover-border: var(--ide-btn-default-hover-border);
- --ide-btn-danger-secondary-hover-border-width: var(--ide-btn-default-hover-border-width);
- --ide-btn-danger-secondary-focus-box-shadow: var(--ide-btn-default-focus-box-shadow);
-
- --ide-btn-disabled-background: transparent;
- --ide-btn-disabled-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border: rgba(223, 223, 223, 0.24);
- --ide-btn-disabled-hover-border-width: 1px;
- --ide-btn-disabled-focus-box-shadow: transparent;
- --ide-btn-disabled-color: rgba(145, 145, 145, 0.48);
-
- --ide-dropdown-background: #004c61;
- --ide-dropdown-hover-background: #00617a;
-
- --ide-dropdown-btn-hover-border: #e9ecef;
- --ide-dropdown-btn-hover-background: var(--ide-background-hover);
-
- --ide-file-row-btn-hover-background: #005a73;
-
- --ide-diff-insert: rgba(155, 185, 85, 0.2);
- --ide-diff-remove: rgba(255, 0, 0, 0.2);
-
- --ide-animation-gradient-1: var(--ide-file-row-btn-hover-background);
- --ide-animation-gradient-2: var(--ide-dropdown-hover-background);
-}
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-light.scss b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-light.scss
deleted file mode 100644
index 26d55dfe2d0..00000000000
--- a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-light.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-// -------
-// Please see `app/assets/stylesheets/page_bundles/ide_themes/README.md` for a guide on contributing new themes
-// -------
-.ide.theme-solarized-light {
- --ide-border-color: #ded7c1;
- --ide-border-color-alt: #dfd7bf;
- --ide-highlight-accent: #5c4e21;
- --ide-text-color: #616161;
- --ide-text-color-secondary: #526f76;
- --ide-background: #ddd6c1;
- --ide-background-hover: #d3cbb7;
- --ide-highlight-background: #eee8d5;
- --ide-link-color: #955800;
- --ide-footer-background: #efe8d3;
- --ide-empty-state-background: #fef6e1;
-
- --ide-input-border: #c0b9a4;
- --ide-input-background: transparent;
-
- --ide-btn-default-background: transparent;
- --ide-btn-default-border: #c0b9a4;
- --ide-btn-default-hover-border: #c0b9a4;
-
- --ide-btn-primary-background: #b16802;
- --ide-btn-primary-border: #a35f00;
- --ide-btn-primary-hover-border: #955800;
- --ide-btn-primary-hover-border-width: 2px;
- --ide-btn-primary-focus-box-shadow: 0 0 0 1px #dd8101;
-
- --ide-btn-danger-secondary-background: transparent;
-
- --ide-btn-disabled-background: transparent;
- --ide-btn-disabled-border: rgba(192, 185, 64, 0.48);
- --ide-btn-disabled-hover-border: rgba(192, 185, 64, 0.48);
- --ide-btn-disabled-hover-border-width: 1px;
- --ide-btn-disabled-focus-box-shadow: transparent;
- --ide-btn-disabled-color: rgba(82, 82, 82, 0.48);
-
- --ide-dropdown-background: #fef6e1;
- --ide-dropdown-hover-background: #efe8d3;
-
- --ide-dropdown-btn-hover-border: #dfd7bf;
- --ide-dropdown-btn-hover-background: #efe8d3;
-
- --ide-file-row-btn-hover-background: #ded6be;
-
- --ide-animation-gradient-1: #d3cbb3;
- --ide-animation-gradient-2: #efe8d3;
-
- .ide-empty-state,
- .ide-sidebar,
- .ide-commit-empty-state {
- img {
- filter: sepia(1) brightness(0.7);
- }
- }
-}
diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb
index c0906cb7ade..018dd43219b 100644
--- a/app/helpers/sessions_helper.rb
+++ b/app/helpers/sessions_helper.rb
@@ -13,9 +13,7 @@ module SessionsHelper
end
def remember_me_enabled?
- return false if session_expire_from_init_enabled?
-
- Gitlab::CurrentSettings.remember_me_enabled?
+ Gitlab::CurrentSettings.allow_user_remember_me?
end
def unconfirmed_verification_email?(user)
@@ -37,11 +35,4 @@ module SessionsHelper
update_email_path: users_update_email_path
}
end
-
- private
-
- def session_expire_from_init_enabled?
- Feature.enabled?(:session_expire_from_init, :instance) &&
- Gitlab::CurrentSettings.session_expire_from_init
- end
end
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index b7f97b1c28b..e42313451e7 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -76,8 +76,7 @@ class ActiveSession
session_private_id = request.session.id.private_id
client = Gitlab::SafeDeviceDetector.new(request.user_agent)
timestamp = Time.current
- key = key_name(user.id, session_private_id)
- expiry = expiry_time(key)
+ expiry = Settings.gitlab['session_expire_delay'] * 60
active_user_session = new(
ip_address: request.remote_ip,
@@ -95,7 +94,7 @@ class ActiveSession
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.pipelined do |pipeline|
pipeline.setex(
- key,
+ key_name(user.id, session_private_id),
expiry,
active_user_session.dump
)
@@ -170,26 +169,6 @@ class ActiveSession
"#{Gitlab::Redis::Sessions::SESSION_NAMESPACE}:#{session_id}"
end
- def self.expiry_time(key)
- # initialize to defaults
- ttl = Settings.gitlab['session_expire_delay'] * 60
-
- return ttl unless Feature.enabled?(:session_expire_from_init, :instance) &&
- Gitlab::CurrentSettings.session_expire_from_init
-
- # If we're initializing a session, there won't already be a session
- # Only use current session TTL if we have expire session from init enabled
- Gitlab::Redis::Sessions.with do |redis|
- # redis returns -2 if the key doesn't exist, -1 if no TTL
- ttl_expire = redis.ttl(key)
-
- # for new sessions, return default ttl, otherwise, keep same ttl
- ttl = ttl_expire if ttl_expire > -1
- end
-
- ttl
- end
-
def self.key_name(user_id, session_id = '*')
"#{Gitlab::Redis::Sessions::USER_SESSIONS_NAMESPACE}::v2:#{user_id}:#{session_id}"
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 80acdcdd11a..df1f4b97bcd 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1165,6 +1165,18 @@ class ApplicationSetting < ApplicationRecord
ChronicDuration.output(value, format: :long) if value
end
+ def allow_user_remember_me?
+ return false if session_expire_from_init_enabled?
+
+ remember_me_enabled?
+ end
+
+ # check the model first, as this will be false on most instances
+ # only check Redis / FF if setting is enabled
+ def session_expire_from_init_enabled?
+ session_expire_from_init? && Feature.enabled?(:session_expire_from_init, :instance)
+ end
+
private
def parsed_grafana_url
diff --git a/app/models/user.rb b/app/models/user.rb
index e3ca34be5c9..8cfa37f2116 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1220,7 +1220,7 @@ class User < ApplicationRecord
# to the session cookie. When remember me is disabled this method ensures these
# values aren't set.
def remember_me!
- super if ::Gitlab::Database.read_write? && ::Gitlab::CurrentSettings.remember_me_enabled?
+ super if ::Gitlab::Database.read_write? && ::Gitlab::CurrentSettings.allow_user_remember_me?
end
def forget_me!
@@ -1246,7 +1246,7 @@ class User < ApplicationRecord
# and compares to the stored value. When remember me is disabled this method ensures
# the upstream comparison does not happen.
def remember_me?(token, generated_at)
- return false unless ::Gitlab::CurrentSettings.remember_me_enabled?
+ return false unless ::Gitlab::CurrentSettings.allow_user_remember_me?
super
end
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index b57b845ed45..585363503db 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -6,6 +6,8 @@
- closable = %w[alert notice success]
.flash-container.flash-container-page.sticky{ data: { testid: 'flash-container' }, class: flash_container_class }
- flash.each do |key, value|
+ - next if key == 'timedout' # used by Warden for state tracking, not meant to be rendered
+
- if key == 'toast' && value
-# Frontend renders toast messages as text and not as HTML.
-# Since toast messages are escaped on the backend via `safe_format` we
diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb
index 609acbb4eca..95c784a8da3 100644
--- a/config/initializers/warden.rb
+++ b/config/initializers/warden.rb
@@ -3,6 +3,7 @@
Rails.application.configure do |config|
Warden::Manager.after_set_user(scope: :user) do |user, auth, opts|
Gitlab::Auth::UniqueIpsLimiter.limit_user!(user)
+ Gitlab::Auth::SessionExpireFromInitEnforcer.new(auth, opts).enforce!
activity = Gitlab::Auth::Activity.new(opts)
@@ -24,6 +25,7 @@ Rails.application.configure do |config|
Warden::Manager.after_authentication(scope: :user) do |user, auth, opts|
ActiveSession.cleanup(user)
ActiveSession.set_marketing_user_cookies(auth, user) if ::Gitlab.ee? && ::Gitlab.com?
+ Gitlab::Auth::SessionExpireFromInitEnforcer.new(auth, opts).set_login_time
Gitlab::AnonymousSession.new(auth.request.remote_ip).cleanup_session_per_ip_count
end
diff --git a/db/migrate/20250417141103_update_default_package_metadata_purl_types_pub.rb b/db/migrate/20250417141103_update_default_package_metadata_purl_types_pub.rb
new file mode 100644
index 00000000000..7ca919c457f
--- /dev/null
+++ b/db/migrate/20250417141103_update_default_package_metadata_purl_types_pub.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class UpdateDefaultPackageMetadataPurlTypesPub < Gitlab::Database::Migration[2.2]
+ milestone '18.0'
+
+ disable_ddl_transaction!
+
+ PARTIALLY_ENABLED_SYNC = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].freeze
+ FULLY_ENABLED_SYNC = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17].freeze
+
+ def change
+ change_column_default :application_settings, :package_metadata_purl_types,
+ from: PARTIALLY_ENABLED_SYNC, to: FULLY_ENABLED_SYNC
+ end
+end
diff --git a/db/migrate/20250417141927_add_pub_purl_type_to_application_setting.rb b/db/migrate/20250417141927_add_pub_purl_type_to_application_setting.rb
new file mode 100644
index 00000000000..3fe26d507f5
--- /dev/null
+++ b/db/migrate/20250417141927_add_pub_purl_type_to_application_setting.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddPubPurlTypeToApplicationSetting < Gitlab::Database::Migration[2.2]
+ milestone '18.0'
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class ApplicationSetting < MigrationRecord
+ end
+
+ PUB_PURL_TYPE = 17
+
+ def up
+ ApplicationSetting.reset_column_information
+ application_setting = ApplicationSetting.last
+ return unless application_setting
+
+ application_setting.package_metadata_purl_types |= [PUB_PURL_TYPE]
+ application_setting.save
+ end
+
+ def down
+ application_setting = ApplicationSetting.last
+ return unless application_setting
+
+ application_setting.package_metadata_purl_types.delete(PUB_PURL_TYPE)
+ application_setting.save
+ end
+end
diff --git a/db/schema_migrations/20250417141103 b/db/schema_migrations/20250417141103
new file mode 100644
index 00000000000..f91d4f78dce
--- /dev/null
+++ b/db/schema_migrations/20250417141103
@@ -0,0 +1 @@
+9ff093a72d79fcfb38acae568c7fcd605fe1fd14a76ed86d935e3734bdaff21d
\ No newline at end of file
diff --git a/db/schema_migrations/20250417141927 b/db/schema_migrations/20250417141927
new file mode 100644
index 00000000000..2a7c5891fbc
--- /dev/null
+++ b/db/schema_migrations/20250417141927
@@ -0,0 +1 @@
+86f791a0f34d1c5ee7e707673022030565ef95db06289f5955cc59a5674d0f6f
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 50a48307d45..bada3ef3aef 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -8957,7 +8957,7 @@ CREATE TABLE application_settings (
encrypted_product_analytics_configurator_connection_string bytea,
encrypted_product_analytics_configurator_connection_string_iv bytea,
silent_mode_enabled boolean DEFAULT false NOT NULL,
- package_metadata_purl_types smallint[] DEFAULT '{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}'::smallint[],
+ package_metadata_purl_types smallint[] DEFAULT '{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17}'::smallint[],
ci_max_includes integer DEFAULT 150 NOT NULL,
remember_me_enabled boolean DEFAULT true NOT NULL,
diagramsnet_enabled boolean DEFAULT true NOT NULL,
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 5c3c5f71c9a..cc9dbfd3f01 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -173,9 +173,9 @@ The following metrics are available:
| `gitlab_diffs_render_real_duration_seconds` | Histogram | 15.8 | Duration in seconds spent on serializing and rendering diffs on diffs batch request | `controller`, `action`, `endpoint_id` |
| `gitlab_memwd_violations_total` | Counter | 15.9 | Total number of times a Ruby process violated a memory threshold | |
| `gitlab_memwd_violations_handled_total` | Counter | 15.9 | Total number of times Ruby process memory violations were handled | |
-| `gitlab_sli_rails_request_apdex_total` | Counter | 14.4 | Total number of request Apdex measurements. For more information, see [Rails request SLIs](../../../development/application_slis/rails_request.md) | `endpoint_id`, `feature_category`, `request_urgency` |
+| `gitlab_sli_rails_request_apdex_total` | Counter | 14.4 | Total number of request Apdex measurements. | `endpoint_id`, `feature_category`, `request_urgency` |
| `gitlab_sli_rails_request_apdex_success_total` | Counter | 14.4 | Total number of successful requests that met the target duration for their urgency. Divide by `gitlab_sli_rails_requests_apdex_total` to get a success ratio | `endpoint_id`, `feature_category`, `request_urgency` |
-| `gitlab_sli_rails_request_error_total` | Counter | 15.7 | Total number of request error measurements. For more information, see [Rails request SLIs](../../../development/application_slis/rails_request.md) | `endpoint_id`, `feature_category`, `request_urgency`, `error` |
+| `gitlab_sli_rails_request_error_total` | Counter | 15.7 | Total number of request error measurements. | `endpoint_id`, `feature_category`, `request_urgency`, `error` |
| `job_register_attempts_failed_total` | Counter | 9.5 | Counts the times a runner fails to register a job | |
| `job_register_attempts_total` | Counter | 9.5 | Counts the times a runner tries to register a job | |
| `job_queue_duration_seconds` | Histogram | 9.5 | Request handling execution time | |
diff --git a/doc/api/experiments.md b/doc/api/experiments.md
index 51887e57b47..07a458fa98e 100644
--- a/doc/api/experiments.md
+++ b/doc/api/experiments.md
@@ -12,7 +12,7 @@ title: Experiments API
{{< /details >}}
-Use this API to interact with A/B experiments. For more information, see the [experiment guide](../development/experiment_guide/_index.md). This API is for internal use only.
+Use this API to interact with A/B experiments. This API is for internal use only.
Prerequisites:
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index 32c254d6a1f..c7d46866470 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -42119,7 +42119,6 @@ Represents a workspaces agent config.
| `annotations` | [`[KubernetesAnnotation!]!`](#kubernetesannotation) | Annotations to apply to Kubernetes objects. |
| `clusterAgent` | [`ClusterAgent!`](#clusteragent) | Cluster agent that the workspaces agent config belongs to. |
| `createdAt` | [`Time!`](#time) | Timestamp of when the workspaces agent config was created. |
-| `defaultMaxHoursBeforeTermination` {{< icon name="warning-solid" >}} | [`Int!`](#int) | **Deprecated** in GitLab 17.9. Field is not used. |
| `defaultResourcesPerWorkspaceContainer` {{< icon name="warning-solid" >}} | [`WorkspaceResources!`](#workspaceresources) | **Introduced** in GitLab 17.9. **Status**: Experiment. Default cpu and memory resources of the workspace container. |
| `defaultRuntimeClass` | [`String!`](#string) | Default Kubernetes RuntimeClass. |
| `dnsZone` | [`String!`](#string) | DNS zone where workspaces are available. |
@@ -42128,7 +42127,6 @@ Represents a workspaces agent config.
| `id` | [`RemoteDevelopmentWorkspacesAgentConfigID!`](#remotedevelopmentworkspacesagentconfigid) | Global ID of the workspaces agent config. |
| `imagePullSecrets` {{< icon name="warning-solid" >}} | [`[ImagePullSecrets!]!`](#imagepullsecrets) | **Introduced** in GitLab 17.9. **Status**: Experiment. Kubernetes secrets to pull private images for a workspace. |
| `labels` | [`[KubernetesLabel!]!`](#kuberneteslabel) | Labels to apply to Kubernetes objects. |
-| `maxHoursBeforeTerminationLimit` {{< icon name="warning-solid" >}} | [`Int!`](#int) | **Deprecated** in GitLab 17.9. Field is not used. |
| `maxResourcesPerWorkspace` {{< icon name="warning-solid" >}} | [`WorkspaceResources!`](#workspaceresources) | **Introduced** in GitLab 17.9. **Status**: Experiment. Maximum cpu and memory resources of the workspace. |
| `networkPolicyEgress` {{< icon name="warning-solid" >}} | [`[NetworkPolicyEgress!]!`](#networkpolicyegress) | **Introduced** in GitLab 17.9. **Status**: Experiment. IP CIDR range specifications for egress destinations from a workspace. |
| `networkPolicyEnabled` | [`Boolean!`](#boolean) | Whether the network policy of the workspaces agent config is enabled. |
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 7c4dfd3e20e..6806bdfe0b2 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -11,7 +11,6 @@ To get started with Vue, read through [their documentation](https://v2.vuejs.org
What is described in the following sections can be found in these examples:
-- [Web IDE](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/app/assets/javascripts/ide/stores)
- [Security products](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/app/assets/javascripts/vue_shared/security_reports)
- [Registry](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/app/assets/javascripts/registry/stores)
diff --git a/doc/user/workspace/configuration.md b/doc/user/workspace/configuration.md
index a7d11dc6134..a3a4433af99 100644
--- a/doc/user/workspace/configuration.md
+++ b/doc/user/workspace/configuration.md
@@ -48,11 +48,13 @@ If you use AWS, you can use our OpenTofu tutorial. For more information, see
{{< history >}}
+- **Time before automatic termination** [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120168) in GitLab 16.0
- Support for private projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124273) in GitLab 16.4.
- **Git reference** and **Devfile location** [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/392382) in GitLab 16.10.
- **Time before automatic termination** [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/392382) to **Workspace automatically terminates after** in GitLab 16.10.
- **Variables** [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/463514) in GitLab 17.1.
- **Workspace automatically terminates after** [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166065) in GitLab 17.6.
+- **Workspace can be created from Merge Request page** [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/187320) in GitLab 18.0.
{{< /history >}}
@@ -67,7 +69,9 @@ Prerequisites:
- You must [set up workspace infrastructure](#set-up-workspace-infrastructure).
- You must have at least the Developer role for the workspace and agent projects.
-To create a workspace:
+{{< tabs >}}
+
+{{< tab title="From a project" >}}
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Edit > New workspace**.
@@ -75,14 +79,33 @@ To create a workspace:
1. From the **Git reference** dropdown list, select the branch, tag, or commit hash
GitLab uses to create the workspace.
1. From the **Devfile** dropdown list, select one of the following:
-
- [GitLab default devfile](_index.md#gitlab-default-devfile).
- [Custom devfile](_index.md#custom-devfile).
-
1. In **Variables**, enter the keys and values of the environment variables you want to inject into the workspace.
To add a new variable, select **Add variable**.
1. Select **Create workspace**.
+{{< /tab >}}
+
+{{< tab title="From a merge request" >}}
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. On the left sidebar, Select **Code > Merge requests**.
+1. Select **Code > Open in Workspace**.
+1. From the **Cluster agent** dropdown list, select a cluster agent owned by the group the project belongs to.
+1. From the **Git reference** dropdown list, select the branch, tag, or commit hash
+ GitLab uses to create the workspace. By default this is the source branch of the merge request.
+1. From the **Devfile** dropdown list, select one of the following:
+ - [GitLab default devfile](_index.md#gitlab-default-devfile).
+ - [Custom devfile](_index.md#custom-devfile).
+1. In **Variables**, enter the keys and values of the environment variables you want to inject into the workspace.
+ To add a new variable, select **Add variable**.
+1. Select **Create workspace**.
+
+{{< /tab >}}
+
+{{< /tabs >}}
+
The workspace might take a few minutes to start.
To open the workspace, under **Preview**, select the workspace.
You also have access to the terminal and can install any necessary dependencies.
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 6976b570363..ad9957b11ea 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -668,16 +668,6 @@ export default [
},
},
- // Web IDE config
- {
- files: ['app/assets/javascripts/ide/**/*.{js,vue}'],
-
- rules: {
- // https://gitlab.com/gitlab-org/gitlab/issues/33024
- 'promise/no-nesting': 'off',
- },
- },
-
// Jest config
jestConfig,
diff --git a/lib/gitlab/auth/session_expire_from_init_enforcer.rb b/lib/gitlab/auth/session_expire_from_init_enforcer.rb
new file mode 100644
index 00000000000..77fc94c9cf6
--- /dev/null
+++ b/lib/gitlab/auth/session_expire_from_init_enforcer.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+# code inspired by Devise Timeoutable
+# https://github.com/heartcombo/devise/blob/fec67f98f26fcd9a79072e4581b1bd40d0c7fa1d/lib/devise/hooks/timeoutable.rb#L8
+module Gitlab
+ module Auth
+ class SessionExpireFromInitEnforcer
+ SESSION_NAMESPACE = :sefie
+
+ attr_reader :warden, :opts
+
+ def self.session_expires_at(controller_session = Session.current)
+ warden_session = controller_session['warden.user.user.session']
+ session = Gitlab::NamespacedSessionStore.new(SESSION_NAMESPACE, warden_session)
+ signed_in_at = session['signed_in_at']
+ return 0 unless signed_in_at.present?
+
+ signed_in_at + timeout_value
+ end
+
+ def self.enabled?
+ Feature.enabled?(:session_expire_from_init, :instance) &&
+ Gitlab::CurrentSettings.session_expire_from_init
+ end
+
+ def self.timeout_value
+ Gitlab::CurrentSettings.session_expire_delay * 60
+ end
+
+ def initialize(warden, opts)
+ @warden = warden
+ @opts = opts
+ end
+
+ def enabled?
+ self.class.enabled?
+ end
+
+ def set_login_time
+ return unless enabled?
+
+ set_signed_in_at
+ end
+
+ def enforce!
+ return unless enabled?
+
+ signed_in_at = session['signed_in_at']
+
+ # immediately after the setting is enabled, users may not have this value set
+ # we set it here so users don't have to log out and log back in to set the expiry
+ unless signed_in_at.present?
+ set_signed_in_at
+ return
+ end
+
+ time_since_sign_in = Time.current.utc.to_i - signed_in_at
+
+ return unless time_since_sign_in > timeout_value
+
+ ::Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
+ throw :warden, scope: scope, message: :timeout # rubocop:disable Cop/BanCatchThrow -- this is called from a Warden hook, which depends on throw :warden to halt and redirect
+ end
+
+ private
+
+ def set_signed_in_at
+ session['signed_in_at'] = Time.current.utc.to_i
+ end
+
+ def timeout_value
+ self.class.timeout_value
+ end
+
+ def proxy
+ @proxy ||= ::Devise::Hooks::Proxy.new(warden)
+ end
+
+ def scope
+ opts[:scope]
+ end
+
+ def session
+ return @session if @session
+
+ session = warden.session(scope)
+ @session = Gitlab::NamespacedSessionStore.new(SESSION_NAMESPACE, session)
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bbd9f7001b1..9aba53ec3d7 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1518,9 +1518,6 @@ msgstr ""
msgid "%{strong_start}no%{strong_end}"
msgstr ""
-msgid "%{tabname} changed"
-msgstr ""
-
msgid "%{tags} tag per image name"
msgstr ""
@@ -6269,9 +6266,6 @@ msgstr ""
msgid "All branches"
msgstr ""
-msgid "All changes are committed"
-msgstr ""
-
msgid "All child items must be confidential in order to turn on confidentiality."
msgstr ""
@@ -6798,9 +6792,6 @@ msgstr ""
msgid "An error occurred creating the blob"
msgstr ""
-msgid "An error occurred creating the new branch."
-msgstr ""
-
msgid "An error occurred deleting the group. Please refresh the page to try again."
msgstr ""
@@ -6969,18 +6960,12 @@ msgstr ""
msgid "An error occurred while fetching the job log. The information presented below may not be accurate. Refresh the page to retrieve the latest job log."
msgstr ""
-msgid "An error occurred while fetching the job logs."
-msgstr ""
-
msgid "An error occurred while fetching the job. The information presented below may not be accurate. Refresh the page to retrieve the latest job log."
msgstr ""
msgid "An error occurred while fetching the jobs."
msgstr ""
-msgid "An error occurred while fetching the latest pipeline."
-msgstr ""
-
msgid "An error occurred while fetching the pipelines jobs."
msgstr ""
@@ -6996,9 +6981,6 @@ msgstr ""
msgid "An error occurred while fetching. Please try again."
msgstr ""
-msgid "An error occurred while getting files for - %{branchId}"
-msgstr ""
-
msgid "An error occurred while getting issue counts"
msgstr ""
@@ -7011,9 +6993,6 @@ msgstr ""
msgid "An error occurred while loading a section of this page."
msgstr ""
-msgid "An error occurred while loading all the files."
-msgstr ""
-
msgid "An error occurred while loading chart data"
msgstr ""
@@ -7050,33 +7029,15 @@ msgstr ""
msgid "An error occurred while loading the file"
msgstr ""
-msgid "An error occurred while loading the file content."
-msgstr ""
-
-msgid "An error occurred while loading the file."
-msgstr ""
-
msgid "An error occurred while loading the file. Please try again later."
msgstr ""
msgid "An error occurred while loading the file. Please try again."
msgstr ""
-msgid "An error occurred while loading the merge request changes."
-msgstr ""
-
-msgid "An error occurred while loading the merge request version data."
-msgstr ""
-
-msgid "An error occurred while loading the merge request."
-msgstr ""
-
msgid "An error occurred while loading the notification settings. Please try again."
msgstr ""
-msgid "An error occurred while loading the pipelines jobs."
-msgstr ""
-
msgid "An error occurred while making the request."
msgstr ""
@@ -7232,21 +7193,6 @@ msgstr ""
msgid "An unexpected error occurred trying to submit your comment. Please try again."
msgstr ""
-msgid "An unexpected error occurred while checking the project environment."
-msgstr ""
-
-msgid "An unexpected error occurred while checking the project runners."
-msgstr ""
-
-msgid "An unexpected error occurred while communicating with the Web Terminal."
-msgstr ""
-
-msgid "An unexpected error occurred while starting the Web Terminal."
-msgstr ""
-
-msgid "An unexpected error occurred while stopping the Web Terminal."
-msgstr ""
-
msgid "An unexpected error occurred. Please try again."
msgstr ""
@@ -8699,9 +8645,6 @@ msgstr ""
msgid "Assigned to %{assignee_name}"
msgstr ""
-msgid "Assigned to me"
-msgstr ""
-
msgid "Assigned to you"
msgstr ""
@@ -10517,18 +10460,9 @@ msgstr ""
msgid "Branch"
msgstr ""
-msgid "Branch %{branchName} was not found in this project's repository."
-msgstr ""
-
msgid "Branch %{branch_name} was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
msgstr ""
-msgid "Branch already exists"
-msgstr ""
-
-msgid "Branch changed"
-msgstr ""
-
msgid "Branch created."
msgstr ""
@@ -10556,9 +10490,6 @@ msgstr ""
msgid "Branch name template"
msgstr ""
-msgid "Branch not loaded - %{branchId}"
-msgstr ""
-
msgid "Branch rules"
msgstr ""
@@ -11842,9 +11773,6 @@ msgstr ""
msgid "CICD|instance enabled"
msgstr ""
-msgid "CODEOWNERS rule violation"
-msgstr ""
-
msgid "CONTRIBUTING"
msgstr ""
@@ -12553,12 +12481,6 @@ msgstr ""
msgid "Choose a template"
msgstr ""
-msgid "Choose a template…"
-msgstr ""
-
-msgid "Choose a type…"
-msgstr ""
-
msgid "Choose an option"
msgstr ""
@@ -13298,9 +13220,6 @@ msgstr ""
msgid "Close %{noteable}"
msgstr ""
-msgid "Close %{tabname}"
-msgstr ""
-
msgid "Close design"
msgstr ""
@@ -14965,9 +14884,6 @@ msgstr ""
msgid "Commit (when editing commit message)"
msgstr ""
-msgid "Commit Message"
-msgstr ""
-
msgid "Commit SHA"
msgstr ""
@@ -15103,12 +15019,6 @@ msgstr ""
msgid "Compare changes"
msgstr ""
-msgid "Compare changes with the last commit"
-msgstr ""
-
-msgid "Compare changes with the merge request target branch"
-msgstr ""
-
msgid "Compare revisions"
msgstr ""
@@ -16659,9 +16569,6 @@ msgstr ""
msgid "Configure GitLab"
msgstr ""
-msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
-
msgid "Configure Gitaly timeouts."
msgstr ""
@@ -16689,9 +16596,6 @@ msgstr ""
msgid "Configure Sentry integration for error tracking"
msgstr ""
-msgid "Configure a %{codeStart}.gitlab-webide.yml%{codeEnd} file in the %{codeStart}.gitlab%{codeEnd} directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
-
msgid "Configure advanced permissions"
msgstr ""
@@ -16902,9 +16806,6 @@ msgstr ""
msgid "Connecting"
msgstr ""
-msgid "Connecting to terminal sync service"
-msgstr ""
-
msgid "Connecting…"
msgstr ""
@@ -18186,18 +18087,12 @@ msgstr ""
msgid "Could not change HEAD: branch '%{branch}' does not exist"
msgstr ""
-msgid "Could not commit. An unexpected error occurred."
-msgstr ""
-
msgid "Could not connect to FogBugz, check your URL"
msgstr ""
msgid "Could not connect to Sentry. Refresh the page to try again."
msgstr ""
-msgid "Could not connect to Web IDE file mirror service."
-msgstr ""
-
msgid "Could not create Wiki Repository at this time. Please try again later."
msgstr ""
@@ -18372,12 +18267,6 @@ msgstr ""
msgid "Create a new %{codeStart}.gitlab-ci.yml%{codeEnd} file at the root of the repository to get started."
msgstr ""
-msgid "Create a new branch"
-msgstr ""
-
-msgid "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes."
-msgstr ""
-
msgid "Create a new fork"
msgstr ""
@@ -18414,9 +18303,6 @@ msgstr ""
msgid "Create commit"
msgstr ""
-msgid "Create commit…"
-msgstr ""
-
msgid "Create common files more quickly, and standardize their format."
msgstr ""
@@ -18429,18 +18315,12 @@ msgstr ""
msgid "Create custom type"
msgstr ""
-msgid "Create directory"
-msgstr ""
-
msgid "Create empty repository"
msgstr ""
msgid "Create epic"
msgstr ""
-msgid "Create file"
-msgstr ""
-
msgid "Create from"
msgstr ""
@@ -18477,27 +18357,15 @@ msgstr ""
msgid "Create new %{name} by email"
msgstr ""
-msgid "Create new branch"
-msgstr ""
-
msgid "Create new confidential %{issuableType}"
msgstr ""
-msgid "Create new directory"
-msgstr ""
-
msgid "Create new domain"
msgstr ""
msgid "Create new emoji"
msgstr ""
-msgid "Create new file"
-msgstr ""
-
-msgid "Create new file or directory"
-msgstr ""
-
msgid "Create new label"
msgstr ""
@@ -18798,9 +18666,6 @@ msgstr ""
msgid "Created branch '%{branch_name}' and a merge request to resolve this issue."
msgstr ""
-msgid "Created by me"
-msgstr ""
-
msgid "Created by:"
msgstr ""
@@ -19008,9 +18873,6 @@ msgstr ""
msgid "Current"
msgstr ""
-msgid "Current Branch"
-msgstr ""
-
msgid "Current Project"
msgstr ""
@@ -22631,21 +22493,9 @@ msgstr ""
msgid "Discard"
msgstr ""
-msgid "Discard all changes"
-msgstr ""
-
-msgid "Discard all changes?"
-msgstr ""
-
msgid "Discard changes"
msgstr ""
-msgid "Discard changes to %{path}?"
-msgstr ""
-
-msgid "Discard draft"
-msgstr ""
-
msgid "Discard pending review?"
msgstr ""
@@ -23532,9 +23382,6 @@ msgstr ""
msgid "Edit file"
msgstr ""
-msgid "Edit files in the editor and commit changes here"
-msgstr ""
-
msgid "Edit fork in Web IDE"
msgstr ""
@@ -24777,9 +24624,6 @@ msgstr ""
msgid "Error fetching labels."
msgstr ""
-msgid "Error fetching merge requests for %{branchId}"
-msgstr ""
-
msgid "Error fetching network graph."
msgstr ""
@@ -24798,12 +24642,6 @@ msgstr ""
msgid "Error linking identity: Provider and Extern UID must be in the session."
msgstr ""
-msgid "Error loading branch data. Please try again."
-msgstr ""
-
-msgid "Error loading branches."
-msgstr ""
-
msgid "Error loading burndown chart data"
msgstr ""
@@ -24816,27 +24654,12 @@ msgstr ""
msgid "Error loading iterations"
msgstr ""
-msgid "Error loading last commit."
-msgstr ""
-
msgid "Error loading markdown preview"
msgstr ""
-msgid "Error loading merge requests."
-msgstr ""
-
msgid "Error loading milestone tab"
msgstr ""
-msgid "Error loading project data. Please try again."
-msgstr ""
-
-msgid "Error loading template types."
-msgstr ""
-
-msgid "Error loading template."
-msgstr ""
-
msgid "Error loading viewer"
msgstr ""
@@ -24891,9 +24714,6 @@ msgstr ""
msgid "Error saving label update."
msgstr ""
-msgid "Error setting up editor. Please try again."
-msgstr ""
-
msgid "Error tracking"
msgstr ""
@@ -24912,9 +24732,6 @@ msgstr ""
msgid "Error uploading file. Please try again."
msgstr ""
-msgid "Error while loading the merge request. Please try again."
-msgstr ""
-
msgid "Error while migrating %{upload_id}: %{error_message}"
msgstr ""
@@ -26480,9 +26297,6 @@ msgstr ""
msgid "File suppressed by a .gitattributes entry or the file's encoding is unsupported."
msgstr ""
-msgid "File templates"
-msgstr ""
-
msgid "File too large. Secure files must be less than %{limit} MB."
msgstr ""
@@ -30991,12 +30805,6 @@ msgstr ""
msgid "IDE|Cannot open Web IDE"
msgstr ""
-msgid "IDE|Commit"
-msgstr ""
-
-msgid "IDE|Commit to %{branchName} branch"
-msgstr ""
-
msgid "IDE|Confirm"
msgstr ""
@@ -31006,18 +30814,12 @@ msgstr ""
msgid "IDE|Could not find a callback URL entry for %{expectedCallbackUrl}."
msgstr ""
-msgid "IDE|Edit"
-msgstr ""
-
msgid "IDE|Editing this application might affect the functionality of the Web IDE. Ensure the configuration meets the following conditions:"
msgstr ""
msgid "IDE|GitLab logo"
msgstr ""
-msgid "IDE|Go to project"
-msgstr ""
-
msgid "IDE|OAuth Callback URLs"
msgstr ""
@@ -31033,15 +30835,6 @@ msgstr ""
msgid "IDE|Restore to default"
msgstr ""
-msgid "IDE|Review"
-msgstr ""
-
-msgid "IDE|Start a new merge request"
-msgstr ""
-
-msgid "IDE|Successful commit"
-msgstr ""
-
msgid "IDE|The %{boldStart}Confidential%{boldEnd} checkbox is cleared."
msgstr ""
@@ -31057,12 +30850,6 @@ msgstr ""
msgid "IDE|The redirect URI path is %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI is %{codeBlockStart}%{example}%{codeBlockEnd}."
msgstr ""
-msgid "IDE|This option is disabled because you are not allowed to create merge requests in this project."
-msgstr ""
-
-msgid "IDE|This option is disabled because you don't have write permissions for the current branch."
-msgstr ""
-
msgid "IDs with errors: %{error_messages}."
msgstr ""
@@ -35330,9 +35117,6 @@ msgstr ""
msgid "LFSStatus|Enabled"
msgstr ""
-msgid "LICENSE"
-msgstr ""
-
msgid "Label"
msgid_plural "Labels"
msgstr[0] ""
@@ -35558,9 +35342,6 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
-msgid "Latest changes"
-msgstr ""
-
msgid "Latest pipeline for the most recent commit on this ref"
msgstr ""
@@ -35624,9 +35405,6 @@ msgstr ""
msgid "Learn more about Service Desk"
msgstr ""
-msgid "Learn more about Web Terminal"
-msgstr ""
-
msgid "Learn more about X.509 signed commits"
msgstr ""
@@ -36273,9 +36051,6 @@ msgstr ""
msgid "Locked"
msgstr ""
-msgid "Locked by %{fileLockUserName}"
-msgstr ""
-
msgid "Locked by %{locker}"
msgstr ""
@@ -36399,9 +36174,6 @@ msgstr ""
msgid "Make adjustments to how your GitLab instance is set up."
msgstr ""
-msgid "Make and review changes in the browser with the Web IDE"
-msgstr ""
-
msgid "Make changes to the diagram"
msgstr ""
@@ -40238,9 +40010,6 @@ msgstr ""
msgid "No branch selected"
msgstr ""
-msgid "No branches found"
-msgstr ""
-
msgid "No change"
msgstr ""
@@ -40331,9 +40100,6 @@ msgstr ""
msgid "No file selected"
msgstr ""
-msgid "No files"
-msgstr ""
-
msgid "No files found."
msgstr ""
@@ -40397,15 +40163,9 @@ msgstr ""
msgid "No memberships found"
msgstr ""
-msgid "No merge requests found"
-msgstr ""
-
msgid "No merge requests match this list."
msgstr ""
-msgid "No messages were logged"
-msgstr ""
-
msgid "No milestone"
msgstr ""
@@ -42304,9 +42064,6 @@ msgstr ""
msgid "Open in Workspace"
msgstr ""
-msgid "Open in file view"
-msgstr ""
-
msgid "Open in full page"
msgstr ""
@@ -43776,9 +43533,6 @@ msgstr ""
msgid "Parse error: Unexpected input near `%{input}`."
msgstr ""
-msgid "Part of merge request changes"
-msgstr ""
-
msgid "Participants"
msgstr ""
@@ -44786,9 +44540,6 @@ msgstr ""
msgid "Pipelines|Auto DevOps"
msgstr ""
-msgid "Pipelines|Build with confidence"
-msgstr ""
-
msgid "Pipelines|By revoking a trigger token you will break any processes making use of it. Are you sure?"
msgstr ""
@@ -44855,9 +44606,6 @@ msgstr ""
msgid "Pipelines|Get started with GitLab CI/CD"
msgstr ""
-msgid "Pipelines|GitLab CI/CD can automatically build, test, and deploy your code. Let GitLab take care of time consuming tasks, so you can spend more time creating."
-msgstr ""
-
msgid "Pipelines|Go to the pipeline editor"
msgstr ""
@@ -50524,15 +50272,6 @@ msgstr ""
msgid "Removing the bypass will cause the merge request to be blocked if any reviews have requested changes."
msgstr ""
-msgid "Rename file"
-msgstr ""
-
-msgid "Rename folder"
-msgstr ""
-
-msgid "Rename/Move"
-msgstr ""
-
msgid "Render diagrams in your documents using PlantUML."
msgstr ""
@@ -51261,9 +51000,6 @@ msgstr ""
msgid "Response text"
msgstr ""
-msgid "Restart Terminal"
-msgstr ""
-
msgid "Restart merge train pipelines with the merged changes."
msgstr ""
@@ -51342,9 +51078,6 @@ msgstr ""
msgid "Revert this merge request"
msgstr ""
-msgid "Review"
-msgstr ""
-
msgid "Review App|View app"
msgstr ""
@@ -51407,12 +51140,6 @@ msgstr ""
msgid "Reviewers needed"
msgstr ""
-msgid "Reviewing"
-msgstr ""
-
-msgid "Reviewing (merge request !%{mergeRequestId})"
-msgstr ""
-
msgid "Reviews"
msgstr ""
@@ -51527,9 +51254,6 @@ msgstr ""
msgid "Run pipeline"
msgstr ""
-msgid "Run tests against your code live using the Web Terminal"
-msgstr ""
-
msgid "Run this job again in order to create the necessary resources."
msgstr ""
@@ -53602,9 +53326,6 @@ msgstr ""
msgid "Search labels"
msgstr ""
-msgid "Search merge requests"
-msgstr ""
-
msgid "Search milestones"
msgstr ""
@@ -56295,9 +56016,6 @@ msgstr ""
msgid "Select a day"
msgstr ""
-msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
-msgstr ""
-
msgid "Select a group"
msgstr ""
@@ -57402,9 +57120,6 @@ msgstr ""
msgid "Show commits in global search results"
msgstr ""
-msgid "Show complete raw log"
-msgstr ""
-
msgid "Show current status of indexing"
msgstr ""
@@ -58713,9 +58428,6 @@ msgstr ""
msgid "Start Time"
msgstr ""
-msgid "Start Web Terminal"
-msgstr ""
-
msgid "Start a %{new_merge_request} with these changes"
msgstr ""
@@ -58788,9 +58500,6 @@ msgstr ""
msgid "Starting with GitLab 17.7, OpenSSL 3 will be used. All TLS connections require TLS 1.2 or higher. Weaker ciphers are no longer supported. Encryption must have at least of 112 bits of security. %{link_start}Learn more%{link_end}."
msgstr ""
-msgid "Starting..."
-msgstr ""
-
msgid "Starts"
msgstr ""
@@ -59061,9 +58770,6 @@ msgstr ""
msgid "Still loading..."
msgstr ""
-msgid "Stop Terminal"
-msgstr ""
-
msgid "Stop impersonating"
msgstr ""
@@ -59073,9 +58779,6 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Stopping..."
-msgstr ""
-
msgid "Storage"
msgstr ""
@@ -60201,9 +59904,6 @@ msgstr ""
msgid "Terminal for environment"
msgstr ""
-msgid "Terminal sync service is running"
-msgstr ""
-
msgid "Terms"
msgstr ""
@@ -60694,9 +60394,6 @@ msgstr ""
msgid "The change requests must be completed or resolved."
msgstr ""
-msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
-msgstr ""
-
msgid "The comment you are editing has been changed by another user. Would you like to keep your changes and overwrite the new description or discard your changes?"
msgstr ""
@@ -60954,9 +60651,6 @@ msgstr ""
msgid "The latest pipeline for this merge request has failed."
msgstr ""
-msgid "The legacy Vue-based GitLab Web IDE will be removed in GitLab 18.0."
-msgstr ""
-
msgid "The license key is invalid."
msgstr ""
@@ -61026,9 +60720,6 @@ msgstr ""
msgid "The metric must be one of %{metrics}."
msgstr ""
-msgid "The name \"%{name}\" is already taken in this directory."
-msgstr ""
-
msgid "The name of the CI/CD configuration file. A path relative to the root directory is optional (for example %{code_open}my/path/.myfile.yml%{code_close})."
msgstr ""
@@ -62956,9 +62647,6 @@ msgstr ""
msgid "To personalize your GitLab experience, we'd like to know a bit more about you. We won't share this information with anyone."
msgstr ""
-msgid "To prepare for this removal, see %{linkStart}deprecations and removals%{linkEnd}."
-msgstr ""
-
msgid "To prevent your project from being placed in a read-only state, %{manage_storage_link_start}manage your storage use%{storage_link_end} or %{support_link_start}contact support%{link_end} immediately."
msgstr ""
@@ -64282,9 +63970,6 @@ msgstr ""
msgid "Unable to create link to vulnerability"
msgstr ""
-msgid "Unable to create pipeline"
-msgstr ""
-
msgid "Unable to fetch branch list for this project."
msgstr ""
@@ -64866,9 +64551,6 @@ msgstr ""
msgid "Uploaded date"
msgstr ""
-msgid "Uploading changes to terminal"
-msgstr ""
-
msgid "Uploading..."
msgstr ""
@@ -66663,15 +66345,9 @@ msgstr ""
msgid "View job currently using resource"
msgstr ""
-msgid "View jobs"
-msgstr ""
-
msgid "View labels"
msgstr ""
-msgid "View log"
-msgstr ""
-
msgid "View merge requests you're involved with from start to finish by highlighting those that Needs Attention and those you are Following."
msgstr ""
@@ -67598,9 +67274,6 @@ msgstr ""
msgid "Web IDE and workspaces"
msgstr ""
-msgid "Web Terminal"
-msgstr ""
-
msgid "Web terminal"
msgstr ""
@@ -67610,36 +67283,12 @@ msgstr ""
msgid "WebAuthn only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
-msgid "WebIDE|Fork project"
-msgstr ""
-
-msgid "WebIDE|Go to fork"
-msgstr ""
-
-msgid "WebIDE|Merge request"
-msgstr ""
-
msgid "WebIDE|Quickly and easily edit multiple files in your project."
msgstr ""
msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open"
msgstr ""
-msgid "WebIDE|This project does not accept unsigned commits."
-msgstr ""
-
-msgid "WebIDE|This project does not accept unsigned commits. You can’t commit changes through the Web IDE."
-msgstr ""
-
-msgid "WebIDE|You can’t edit files directly in this project. Fork this project and submit a merge request with your changes."
-msgstr ""
-
-msgid "WebIDE|You can’t edit files directly in this project. Go to your fork and submit a merge request with your changes."
-msgstr ""
-
-msgid "WebIDE|You need permission to edit files directly in this project."
-msgstr ""
-
msgid "WebIdeOAuthCallback|Close tab"
msgstr ""
@@ -69679,12 +69328,6 @@ msgstr ""
msgid "Workspaces|Your workspaces"
msgstr ""
-msgid "Would you like to create a new branch?"
-msgstr ""
-
-msgid "Would you like to try auto-generating a branch name?"
-msgstr ""
-
msgid "Write"
msgstr ""
@@ -70164,9 +69807,6 @@ msgstr ""
msgid "You do not have permission to run a pipeline on this branch."
msgstr ""
-msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
-msgstr ""
-
msgid "You do not have permission to set a member awaiting"
msgstr ""
@@ -70455,12 +70095,6 @@ msgstr ""
msgid "You will first need to set up Jira Integration to use this feature."
msgstr ""
-msgid "You will lose all changes you've made to this file. This action cannot be undone."
-msgstr ""
-
-msgid "You will lose all uncommitted changes you've made in this project. This action cannot be undone."
-msgstr ""
-
msgid "You will need to update your local repositories to point to the new location."
msgstr ""
@@ -70760,9 +70394,6 @@ msgstr ""
msgid "Your changes can be committed to %{branchName} because a merge request is open."
msgstr ""
-msgid "Your changes have been committed. Commit %{commitId} %{commitStats}"
-msgstr ""
-
msgid "Your changes have been saved"
msgstr ""
@@ -73514,9 +73145,6 @@ msgstr ""
msgid "wiki page"
msgstr ""
-msgid "with %{additions} additions, %{deletions} deletions."
-msgstr ""
-
msgid "with Admin Mode"
msgstr ""
diff --git a/package.json b/package.json
index 9613bca7d01..777be87baa1 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
"@gitlab/fonts": "^1.3.0",
"@gitlab/query-language-rust": "0.7.0",
"@gitlab/svgs": "3.128.0",
- "@gitlab/ui": "113.0.0",
+ "@gitlab/ui": "113.2.0",
"@gitlab/vue-router-vue3": "npm:vue-router@4.5.1",
"@gitlab/vuex-vue3": "npm:vuex@4.1.0",
"@gitlab/web-ide": "^0.0.1-dev-20250414030534",
diff --git a/spec/frontend/editor/source_editor_webide_ext_spec.js b/spec/frontend/editor/source_editor_webide_ext_spec.js
deleted file mode 100644
index 7e4079c17f7..00000000000
--- a/spec/frontend/editor/source_editor_webide_ext_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Emitter } from 'monaco-editor';
-import { setHTMLFixture } from 'helpers/fixtures';
-import { EditorWebIdeExtension } from '~/editor/extensions/source_editor_webide_ext';
-import SourceEditor from '~/editor/source_editor';
-
-describe('Source Editor Web IDE Extension', () => {
- let editorEl;
- let editor;
- let instance;
-
- beforeEach(() => {
- setHTMLFixture('');
- editorEl = document.getElementById('editor');
- editor = new SourceEditor();
- });
-
- describe('onSetup', () => {
- it.each`
- width | renderSideBySide
- ${'0'} | ${false}
- ${'699px'} | ${false}
- ${'700px'} | ${true}
- `(
- "correctly renders the Diff Editor when the parent element's width is $width",
- ({ width, renderSideBySide }) => {
- editorEl.style.width = width;
- instance = editor.createDiffInstance({ el: editorEl });
-
- const sideBySideSpy = jest.spyOn(instance, 'updateOptions');
- instance.use({ definition: EditorWebIdeExtension });
-
- expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide });
- },
- );
-
- it('re-renders the Diff Editor when layout of the modified editor is changed', async () => {
- const emitter = new Emitter();
- editorEl.style.width = '700px';
-
- instance = editor.createDiffInstance({ el: editorEl });
- instance.getModifiedEditor().onDidLayoutChange = emitter.event;
- instance.use({ definition: EditorWebIdeExtension });
-
- const sideBySideSpy = jest.spyOn(instance, 'updateOptions');
- await emitter.fire();
-
- expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide: true });
-
- editorEl.style.width = '0px';
- await emitter.fire();
- expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide: false });
- });
- });
-});
diff --git a/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_connection_status_badge_spec.js b/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_connection_status_badge_spec.js
index e0b021e64d3..2b10239c742 100644
--- a/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_connection_status_badge_spec.js
+++ b/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_connection_status_badge_spec.js
@@ -25,11 +25,11 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_co
const findReconnectTooltip = () => wrapper.findComponent(GlPopover);
describe.each([
- [connectionStatus.connected, 'success', 'connected', undefined, 'Synced'],
- [connectionStatus.disconnected, 'warning', 'retry', '#', 'Refresh'],
- [connectionStatus.connecting, 'muted', 'spinner', undefined, 'Updating'],
+ [connectionStatus.connected, 'success', 'connected', undefined, 'Synced', false],
+ [connectionStatus.disconnected, 'warning', 'retry', '#', 'Refresh', true],
+ [connectionStatus.connecting, 'muted', 'spinner', undefined, 'Updating', false],
// eslint-disable-next-line max-params
- ])('when connection status is %s', (status, variant, icon, href, text) => {
+ ])('when connection status is %s', (status, variant, icon, href, text, shouldReconnect) => {
beforeEach(() => {
createComponent({ connectionStatus: status });
});
@@ -46,5 +46,21 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_co
const tooltip = findReconnectTooltip();
expect(tooltip.props().target).toBe('status-badge-popover-id');
});
+
+ describe('when badge is clicked', () => {
+ beforeEach(() => {
+ findReconnectBadge().vm.$emit('click');
+ });
+
+ if (shouldReconnect) {
+ it('emits reconnect event', () => {
+ expect(wrapper.emitted('reconnect')).toEqual([[]]);
+ });
+ } else {
+ it('does not emit reconnect event', () => {
+ expect(wrapper.emitted('reconnect')).toBeUndefined();
+ });
+ }
+ });
});
});
diff --git a/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_status_bar_spec.js b/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_status_bar_spec.js
index 53f94fea879..c17d9d6edb3 100644
--- a/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_status_bar_spec.js
+++ b/spec/frontend/environments/environment_details/components/kubernetes/kubernetes_status_bar_spec.js
@@ -226,7 +226,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_st
fluxResourceStatus: { conditions: [{ status: 'True', type: 'Ready' }] },
});
- findSyncBadge().trigger('click');
+ findSyncBadge().vm.$emit('click');
expect(wrapper.emitted('show-flux-resource-details')).toBeDefined();
});
diff --git a/spec/frontend/gitlab_version_check/components/gitlab_version_check_badge_spec.js b/spec/frontend/gitlab_version_check/components/gitlab_version_check_badge_spec.js
index fcb6dd1141e..ed3023e94c9 100644
--- a/spec/frontend/gitlab_version_check/components/gitlab_version_check_badge_spec.js
+++ b/spec/frontend/gitlab_version_check/components/gitlab_version_check_badge_spec.js
@@ -27,7 +27,6 @@ describe('GitlabVersionCheckBadge', () => {
unmockTracking();
});
- const findVersionCheckBadge = () => wrapper.findByTestId('check-version-badge');
const findGlBadge = () => wrapper.findComponent(GlBadge);
describe('template', () => {
@@ -61,8 +60,8 @@ describe('GitlabVersionCheckBadge', () => {
expect(findGlBadge().attributes('href')).toBe(UPGRADE_DOCS_URL);
});
- it(`tracks click_version_badge with label ${expectedUI.title} when badge is clicked`, async () => {
- await findVersionCheckBadge().trigger('click');
+ it(`tracks click_version_badge with label ${expectedUI.title} when badge is clicked`, () => {
+ findGlBadge().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
label: 'version_badge',
@@ -88,8 +87,8 @@ describe('GitlabVersionCheckBadge', () => {
expect(findGlBadge().attributes('href')).toBe(undefined);
});
- it('does not track click_version_badge', async () => {
- await findVersionCheckBadge().trigger('click');
+ it('does not track click_version_badge', () => {
+ findGlBadge().vm.$emit('click');
expect(trackingSpy).not.toHaveBeenCalledWith(undefined, 'click_link', {
label: 'version_badge',
diff --git a/spec/frontend/ide/components/activity_bar_spec.js b/spec/frontend/ide/components/activity_bar_spec.js
deleted file mode 100644
index 95582aca8fd..00000000000
--- a/spec/frontend/ide/components/activity_bar_spec.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import { nextTick } from 'vue';
-import { GlBadge } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import ActivityBar from '~/ide/components/activity_bar.vue';
-import { leftSidebarViews } from '~/ide/constants';
-import { createStore } from '~/ide/stores';
-
-const { edit, ...VIEW_OBJECTS_WITHOUT_EDIT } = leftSidebarViews;
-const MODES_WITHOUT_EDIT = Object.keys(VIEW_OBJECTS_WITHOUT_EDIT);
-const MODES = Object.keys(leftSidebarViews);
-
-describe('IDE ActivityBar component', () => {
- let wrapper;
- let store;
-
- const findChangesBadge = () => wrapper.findComponent(GlBadge);
- const findModeButton = (mode) => wrapper.findByTestId(`${mode}-mode-button`);
-
- const mountComponent = (state) => {
- store = createStore();
- store.replaceState({
- ...store.state,
- projects: { abcproject: { web_url: 'testing' } },
- currentProjectId: 'abcproject',
- ...state,
- });
-
- wrapper = shallowMountExtended(ActivityBar, { store });
- };
-
- describe('active item', () => {
- // Test that mode button does not have 'active' class before click,
- // and does have 'active' class after click
- const testSettingActiveItem = async (mode) => {
- const button = findModeButton(mode);
-
- expect(button.classes('active')).toBe(false);
-
- button.trigger('click');
- await nextTick();
-
- expect(button.classes('active')).toBe(true);
- };
-
- it.each(MODES)('is initially set to %s mode', (mode) => {
- mountComponent({ currentActivityView: leftSidebarViews[mode].name });
-
- const button = findModeButton(mode);
-
- expect(button.classes('active')).toBe(true);
- });
-
- it.each(MODES_WITHOUT_EDIT)('is correctly set after clicking %s mode button', (mode) => {
- mountComponent();
-
- testSettingActiveItem(mode);
- });
-
- it('is correctly set after clicking edit mode button', () => {
- // The default currentActivityView is leftSidebarViews.edit.name,
- // so for the 'edit' mode, we pass a different currentActivityView.
- mountComponent({ currentActivityView: leftSidebarViews.review.name });
-
- testSettingActiveItem('edit');
- });
- });
-
- describe('changes badge', () => {
- it('is rendered when files are staged', () => {
- mountComponent({ stagedFiles: [{ path: '/path/to/file' }] });
-
- expect(findChangesBadge().text()).toBe('1');
- });
-
- it('is not rendered when no changes are present', () => {
- mountComponent();
-
- expect(findChangesBadge().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/ide/components/branches/item_spec.js b/spec/frontend/ide/components/branches/item_spec.js
deleted file mode 100644
index bd5dd2ff3f2..00000000000
--- a/spec/frontend/ide/components/branches/item_spec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Item from '~/ide/components/branches/item.vue';
-import { createRouter, EmptyRouterComponent } from '~/ide/ide_router';
-import { createStore } from '~/ide/stores';
-import Timeago from '~/vue_shared/components/time_ago_tooltip.vue';
-import { projectData } from '../../mock_data';
-
-const TEST_BRANCH = {
- name: 'main',
- committedDate: '2018-01-05T05:50Z',
-};
-const TEST_PROJECT_ID = projectData.name_with_namespace;
-
-describe('IDE branch item', () => {
- let wrapper;
- let store;
- let router;
-
- function createComponent(props = {}) {
- wrapper = shallowMount(Item, {
- propsData: {
- item: { ...TEST_BRANCH },
- projectId: TEST_PROJECT_ID,
- isActive: false,
- ...props,
- },
- router,
- });
- }
-
- beforeEach(() => {
- store = createStore();
- router = createRouter(store);
- router.addRoute({
- path: '/',
- component: EmptyRouterComponent,
- });
- });
-
- describe('if not active', () => {
- beforeEach(() => {
- createComponent();
- });
- it('renders branch name and timeago', () => {
- expect(wrapper.text()).toContain(TEST_BRANCH.name);
- expect(wrapper.findComponent(Timeago).props('time')).toBe(TEST_BRANCH.committedDate);
- expect(wrapper.findComponent(GlIcon).exists()).toBe(false);
- });
-
- it('renders link to branch', () => {
- const expectedHref = router.resolve(
- `/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`,
- ).href;
-
- expect(wrapper.text()).toMatch('a');
- expect(wrapper.attributes('href')).toBe(expectedHref);
- });
- });
-
- it('renders icon if is not active', () => {
- createComponent({ isActive: true });
-
- expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
- });
-});
diff --git a/spec/frontend/ide/components/branches/search_list_spec.js b/spec/frontend/ide/components/branches/search_list_spec.js
deleted file mode 100644
index e5a5885bbb3..00000000000
--- a/spec/frontend/ide/components/branches/search_list_spec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-// eslint-disable-next-line no-restricted-imports
-import Vuex from 'vuex';
-import Item from '~/ide/components/branches/item.vue';
-import List from '~/ide/components/branches/search_list.vue';
-import { branches } from '../../mock_data';
-
-Vue.use(Vuex);
-
-describe('IDE branches search list', () => {
- let wrapper;
- const fetchBranchesMock = jest.fn();
-
- const createComponent = (state, currentBranchId = 'branch') => {
- const fakeStore = new Vuex.Store({
- state: {
- currentBranchId,
- currentProjectId: 'project',
- },
- modules: {
- branches: {
- namespaced: true,
- state: { isLoading: false, branches: [], ...state },
- actions: {
- fetchBranches: fetchBranchesMock,
- },
- },
- },
- });
-
- wrapper = shallowMount(List, {
- store: fakeStore,
- });
- };
-
- it('calls fetch on mounted', () => {
- createComponent();
- expect(fetchBranchesMock).toHaveBeenCalled();
- });
-
- it('renders loading icon when `isLoading` is true', () => {
- createComponent({ isLoading: true });
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders branches not found when search is not empty and branches list is empty', async () => {
- createComponent({ branches: [] });
- wrapper.find('input[type="search"]').setValue('something');
-
- await nextTick();
- expect(wrapper.text()).toContain('No branches found');
- });
-
- describe('with branches', () => {
- it('renders list', () => {
- createComponent({ branches });
- const items = wrapper.findAllComponents(Item);
-
- expect(items.length).toBe(branches.length);
- });
-
- it('renders check next to active branch', () => {
- const activeBranch = 'regular';
- createComponent({ branches }, activeBranch);
- const items = wrapper.findAllComponents(Item).filter((w) => w.props('isActive'));
-
- expect(items.length).toBe(1);
- expect(items.at(0).props('item').name).toBe(activeBranch);
- });
- });
-});
diff --git a/spec/frontend/ide/components/cannot_push_code_alert_spec.js b/spec/frontend/ide/components/cannot_push_code_alert_spec.js
deleted file mode 100644
index c72d8c5fccd..00000000000
--- a/spec/frontend/ide/components/cannot_push_code_alert_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { GlButton, GlAlert } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { stubComponent } from 'helpers/stub_component';
-import CannotPushCodeAlert from '~/ide/components/cannot_push_code_alert.vue';
-
-const TEST_MESSAGE = 'Hello test message!';
-const TEST_HREF = '/test/path/to/fork';
-const TEST_BUTTON_TEXT = 'Fork text';
-
-describe('ide/components/cannot_push_code_alert', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(CannotPushCodeAlert, {
- propsData: {
- message: TEST_MESSAGE,
- ...props,
- },
- stubs: {
- GlAlert: {
- ...stubComponent(GlAlert),
- template: `${dummyFile.text.content}
`, - }); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('when files is markdown', () => { - beforeEach(async () => { - await createComponent({ activeFile }); - }); - - it('renders an Edit and a Preview Tab', () => { - const tabs = findTabs(); - - expect(tabs).toHaveLength(2); - expect(tabs.at(0).element.dataset.testid).toBe('edit-tab'); - expect(tabs.at(1).element.dataset.testid).toBe('preview-tab'); - }); - - it('renders markdown for tempFile', async () => { - findPreviewTab().vm.$emit('click'); - await waitForPromises(); - expect(wrapper.findComponent(ContentViewer).html()).toContain(dummyFile.text.content); - }); - - describe('when file changes to non-markdown file', () => { - beforeEach(() => { - wrapper.setProps({ file: dummyFile.empty }); - }); - - it('should hide tabs', () => { - expect(findTabs()).toHaveLength(0); - }); - }); - }); - - it('when not in edit mode, shows no tabs', async () => { - await createComponent({ - state: { - currentActivityView: leftSidebarViews.review.name, - }, - activeFile, - }); - expect(findTabs()).toHaveLength(0); - }); - }); - - describe('when file is binary and not raw', () => { - beforeEach(async () => { - const activeFile = dummyFile.binary; - await createComponent({ activeFile }); - }); - - it('does not render the IDE', () => { - expect(findEditor().isVisible()).toBe(false); - }); - - it('does not create an instance', () => { - expect(createInstanceSpy).not.toHaveBeenCalled(); - expect(createDiffInstanceSpy).not.toHaveBeenCalled(); - }); - }); - - describe('createEditorInstance', () => { - it.each` - viewer | diffInstance - ${viewerTypes.edit} | ${undefined} - ${viewerTypes.diff} | ${true} - ${viewerTypes.mr} | ${true} - `( - 'creates instance of correct type when viewer is $viewer', - async ({ viewer, diffInstance }) => { - await createComponent({ - state: { viewer }, - }); - const isDiff = () => { - return diffInstance ? { isDiff: true } : {}; - }; - expect(createInstanceSpy).toHaveBeenCalledWith(expect.objectContaining(isDiff())); - expect(createDiffInstanceSpy).toHaveBeenCalledTimes((diffInstance && 1) || 0); - }, - ); - - it('installs the WebIDE extension', async () => { - await createComponent(); - expect(applyExtensionSpy).toHaveBeenCalled(); - const ideExtensionApi = extensionsStore.get('EditorWebIde').api; - Reflect.ownKeys(ideExtensionApi).forEach((fn) => { - expect(vm.editor[fn]).toBeDefined(); - expect(vm.editor.methods[fn]).toBe('EditorWebIde'); - }); - }); - }); - - describe('setupEditor', () => { - it('creates new model on load', async () => { - await createComponent(); - // We always create two models per file to be able to build a diff of changes - expect(createModelSpy).toHaveBeenCalledTimes(2); - // The model with the most recent changes is the last one - const [content] = createModelSpy.mock.calls[1]; - expect(content).toBe(dummyFile.text.content); - }); - - it('does not create a new model on subsequent calls to setupEditor and re-uses the already-existing model', async () => { - await createComponent(); - const existingModel = vm.model; - createModelSpy.mockClear(); - - vm.setupEditor(); - - expect(createModelSpy).not.toHaveBeenCalled(); - expect(vm.model).toBe(existingModel); - }); - - it('updates state with the value of the model', async () => { - await createComponent(); - const newContent = 'As Gregor Samsa\n awoke one morning\n'; - vm.model.setValue(newContent); - - vm.setupEditor(); - - expect(vm.file.content).toBe(newContent); - }); - - it('sets head model as staged file', async () => { - await createComponent(); - vm.modelManager.dispose(); - const addModelSpy = jest.spyOn(ModelManager.prototype, 'addModel'); - - vm.$store.state.stagedFiles.push({ ...vm.file, key: 'staged' }); - vm.file.staged = true; - vm.file.key = `unstaged-${vm.file.key}`; - - vm.setupEditor(); - - expect(addModelSpy).toHaveBeenCalledWith(vm.file, vm.$store.state.stagedFiles[0]); - }); - - it.each` - prefix | activeFile | viewer | shouldHaveMarkdownExtension - ${'Should not'} | ${dummyFile.text} | ${viewerTypes.edit} | ${false} - ${'Should'} | ${dummyFile.markdown} | ${viewerTypes.edit} | ${true} - ${'Should not'} | ${dummyFile.empty} | ${viewerTypes.edit} | ${false} - ${'Should not'} | ${dummyFile.text} | ${viewerTypes.diff} | ${false} - ${'Should not'} | ${dummyFile.markdown} | ${viewerTypes.diff} | ${false} - ${'Should not'} | ${dummyFile.empty} | ${viewerTypes.diff} | ${false} - ${'Should not'} | ${dummyFile.text} | ${viewerTypes.mr} | ${false} - ${'Should not'} | ${dummyFile.markdown} | ${viewerTypes.mr} | ${false} - ${'Should not'} | ${dummyFile.empty} | ${viewerTypes.mr} | ${false} - `( - '$prefix install markdown extension for $activeFile.name in $viewer viewer', - async ({ activeFile, viewer, shouldHaveMarkdownExtension } = {}) => { - await createComponent({ state: { viewer }, activeFile }); - - if (shouldHaveMarkdownExtension) { - expect(applyExtensionSpy).toHaveBeenCalledWith({ - definition: EditorMarkdownPreviewExtension, - setupOptions: { previewMarkdownPath: PREVIEW_MARKDOWN_PATH }, - }); - // TODO: spying on extensions causes Jest to blow up, so we have to assert on - // the public property the extension adds, as opposed to the args passed to the ctor - expect(wrapper.vm.editor.markdownPreview.path).toBe(PREVIEW_MARKDOWN_PATH); - } else { - expect(applyExtensionSpy).not.toHaveBeenCalledWith( - wrapper.vm.editor, - expect.any(EditorMarkdownExtension), - ); - } - }, - ); - - it('fetches the live preview extension even if markdown is not the first opened file', async () => { - const textFile = dummyFile.text; - const mdFile = dummyFile.markdown; - const previewExtConfig = { - definition: EditorMarkdownPreviewExtension, - setupOptions: { previewMarkdownPath: PREVIEW_MARKDOWN_PATH }, - }; - await createComponent({ activeFile: textFile }); - applyExtensionSpy.mockClear(); - - await wrapper.setProps({ file: mdFile }); - await waitForPromises(); - - expect(applyExtensionSpy).toHaveBeenCalledWith(previewExtConfig); - }); - }); - - describe('editor tabs', () => { - beforeEach(async () => { - await createComponent(); - }); - - it.each` - mode | isVisible - ${'edit'} | ${false} - ${'review'} | ${false} - ${'commit'} | ${false} - `('tabs in $mode are $isVisible', async ({ mode, isVisible } = {}) => { - vm.$store.state.currentActivityView = leftSidebarViews[mode].name; - - await nextTick(); - expect(wrapper.find('.nav-links').exists()).toBe(isVisible); - }); - }); - - describe('files in preview mode', () => { - const changeViewMode = (viewMode) => - vm.$store.dispatch('editor/updateFileEditor', { - path: vm.file.path, - data: { viewMode }, - }); - - beforeEach(async () => { - await createComponent({ - activeFile: dummyFile.markdown, - }); - - changeViewMode(FILE_VIEW_MODE_PREVIEW); - await nextTick(); - }); - - it('do not show the editor', () => { - expect(vm.showEditor).toBe(false); - expect(findEditor().isVisible()).toBe(false); - }); - }); - - describe('initEditor', () => { - const hideEditorAndRunFn = async () => { - jest.clearAllMocks(); - jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true); - - vm.initEditor(); - await nextTick(); - }; - - it('does not fetch file information for temp entries', async () => { - await createComponent({ - activeFile: dummyFile.text, - }); - - expect(vm.getFileData).not.toHaveBeenCalled(); - }); - - it('is being initialised for files without content even if shouldHideEditor is `true`', async () => { - await createComponent({ - activeFile: dummyFile.empty, - }); - - await hideEditorAndRunFn(); - - expect(vm.getFileData).toHaveBeenCalled(); - expect(vm.getRawFileData).toHaveBeenCalled(); - }); - - it('does not initialize editor for files already with content when shouldHideEditor is `true`', async () => { - await createComponent({ - activeFile: dummyFile.text, - }); - - await hideEditorAndRunFn(); - - expect(vm.getFileData).not.toHaveBeenCalled(); - expect(vm.getRawFileData).not.toHaveBeenCalled(); - expect(createInstanceSpy).not.toHaveBeenCalled(); - }); - }); - - describe('updates on file changes', () => { - beforeEach(async () => { - await createComponent({ - activeFile: createActiveFile({ - content: 'foo', // need to prevent full cycle of initEditor - }), - }); - jest.spyOn(vm, 'initEditor').mockImplementation(); - }); - - it('calls removePendingTab when old file is pending', async () => { - jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true); - jest.spyOn(vm, 'removePendingTab').mockImplementation(); - - const origFile = vm.file; - vm.file.pending = true; - await nextTick(); - - wrapper.setProps({ - file: file('testing'), - }); - vm.file.content = 'foo'; // need to prevent full cycle of initEditor - await nextTick(); - - expect(vm.removePendingTab).toHaveBeenCalledWith(origFile); - }); - - it('does not call initEditor if the file did not change', async () => { - const newFile = { ...store.state.openFiles[0] }; - wrapper.setProps({ - file: newFile, - }); - await nextTick(); - - expect(vm.initEditor).not.toHaveBeenCalled(); - }); - - it('calls initEditor when file key is changed', async () => { - expect(vm.initEditor).not.toHaveBeenCalled(); - - wrapper.setProps({ - file: { - ...vm.file, - key: 'new', - }, - }); - await nextTick(); - - expect(vm.initEditor).toHaveBeenCalled(); - }); - }); - - describe('populates editor with the fetched content', () => { - const createRemoteFile = (name) => ({ - ...file(name), - tmpFile: false, - }); - - beforeEach(async () => { - await createComponent(); - vm.getRawFileData.mockRestore(); - }); - - it('after switching viewer from edit to diff', async () => { - const f = createRemoteFile('newFile'); - store.state.entries[f.path] = f; - jest.spyOn(service, 'getRawFileData').mockImplementation(() => { - expect(vm.file.loading).toBe(true); - - // switching from edit to diff mode usually triggers editor initialization - vm.$store.state.viewer = viewerTypes.diff; - - jest.runOnlyPendingTimers(); - - return Promise.resolve('rawFileData123\n'); - }); - - wrapper.setProps({ - file: f, - }); - - await waitForEditorSetup(); - expect(vm.model.getModel().getValue()).toBe('rawFileData123\n'); - }); - - it('after opening multiple files at the same time', async () => { - const fileA = createRemoteFile('fileA'); - const aContent = 'fileA-rawContent\n'; - const bContent = 'fileB-rawContent\n'; - const fileB = createRemoteFile('fileB'); - store.state.entries[fileA.path] = fileA; - store.state.entries[fileB.path] = fileB; - - jest - .spyOn(service, 'getRawFileData') - .mockImplementation(() => { - // opening fileB while the content of fileA is still being fetched - wrapper.setProps({ - file: fileB, - }); - return Promise.resolve(aContent); - }) - .mockImplementationOnce(() => { - // we delay returning fileB content - // to make sure the editor doesn't initialize prematurely - jest.advanceTimersByTime(30); - return Promise.resolve(bContent); - }); - - wrapper.setProps({ - file: fileA, - }); - - await waitForEditorSetup(); - expect(vm.model.getModel().getValue()).toBe(bContent); - }); - }); - - describe('onPaste', () => { - const setFileName = (name) => - createActiveFile({ - content: 'hello world\n', - name, - path: `foo/${name}`, - key: 'new', - }); - - const pasteImage = () => { - window.dispatchEvent( - Object.assign(new Event('paste'), { - clipboardData: { - files: [new File(['foo'], 'foo.png', { type: 'image/png' })], - }, - }), - ); - }; - - const watchState = (watched) => - new Promise((resolve) => { - const unwatch = vm.$store.watch(watched, () => { - unwatch(); - resolve(); - }); - }); - - // Pasting an image does a lot of things like using the FileReader API, - // so, waitForPromises isn't very reliable (and causes a flaky spec) - // Read more about state.watch: https://vuex.vuejs.org/api/#watch - const waitForFileContentChange = () => watchState((s) => s.entries['foo/bar.md'].content); - - beforeEach(async () => { - await createComponent({ - state: { - trees: { - 'gitlab-org/gitlab': { tree: [] }, - }, - currentProjectId: 'gitlab-org', - currentBranchId: 'gitlab', - }, - activeFile: setFileName('bar.md'), - }); - - // set cursor to line 2, column 1 - vm.editor.setSelection(new Range(2, 1, 2, 1)); - vm.editor.focus(); - - jest.spyOn(vm.editor, 'hasTextFocus').mockReturnValue(true); - }); - - it('adds an image entry to the same folder for a pasted image in a markdown file', async () => { - pasteImage(); - - await waitForFileContentChange(); - expect(vm.$store.state.entries['foo/foo.png'].rawPath.startsWith('blob:')).toBe(true); - expect(vm.$store.state.entries['foo/foo.png']).toMatchObject({ - path: 'foo/foo.png', - type: 'blob', - content: 'foo', - rawPath: vm.$store.state.entries['foo/foo.png'].rawPath, - }); - }); - - it("adds a markdown image tag to the file's contents", async () => { - pasteImage(); - - await waitForFileContentChange(); - expect(vm.file.content).toBe('hello world\n'); - }); - - it("does not add file to state or set markdown image syntax if the file isn't markdown", async () => { - await wrapper.setProps({ - file: setFileName('myfile.txt'), - }); - pasteImage(); - - await waitForPromises(); - expect(vm.$store.state.entries['foo/foo.png']).toBeUndefined(); - expect(vm.file.content).toBe('hello world\n'); - }); - }); - - describe('fetchEditorconfigRules', () => { - it.each(exampleFiles)( - 'does not fetch content from remote for .editorconfig files present locally (case %#)', - async ({ path, monacoRules }) => { - await createComponent({ - state: { - entries: (() => { - const res = {}; - exampleConfigs.forEach(({ path: configPath, content }) => { - res[configPath] = { ...file(), path: configPath, content }; - }); - return res; - })(), - }, - activeFile: createActiveFile({ - path, - key: path, - name: 'myfile.txt', - content: 'hello world', - }), - }); - - expect(vm.rules).toEqual(monacoRules); - expect(vm.model.options).toMatchObject(monacoRules); - expect(vm.getFileData).not.toHaveBeenCalled(); - expect(vm.getRawFileData).not.toHaveBeenCalled(); - }, - ); - - it('fetches content from remote for .editorconfig files not available locally', async () => { - const activeFile = createActiveFile({ - path: 'foo/bar/baz/test/my_spec.js', - key: 'foo/bar/baz/test/my_spec.js', - name: 'myfile.txt', - content: 'hello world', - }); - - const expectations = [ - 'foo/bar/baz/.editorconfig', - 'foo/bar/.editorconfig', - 'foo/.editorconfig', - '.editorconfig', - ]; - - await createComponent({ - state: { - entries: (() => { - const res = { - [activeFile.path]: activeFile, - }; - exampleConfigs.forEach(({ path: configPath }) => { - const f = { ...file(), path: configPath }; - delete f.content; - delete f.raw; - res[configPath] = f; - }); - return res; - })(), - }, - activeFile, - }); - - expect(service.getFileData.mock.calls.map(([args]) => args)).toEqual( - expectations.map((expectation) => expect.stringContaining(expectation)), - ); - expect(service.getRawFileData.mock.calls.map(([args]) => args)).toEqual( - expectations.map((expectation) => expect.objectContaining({ path: expectation })), - ); - }); - }); -}); diff --git a/spec/frontend/ide/components/repo_tab_spec.js b/spec/frontend/ide/components/repo_tab_spec.js deleted file mode 100644 index 08e8062a45a..00000000000 --- a/spec/frontend/ide/components/repo_tab_spec.js +++ /dev/null @@ -1,184 +0,0 @@ -import { GlTab } from '@gitlab/ui'; -import Vue from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { stubComponent } from 'helpers/stub_component'; -import RepoTab from '~/ide/components/repo_tab.vue'; -import { createStore } from '~/ide/stores'; -import { mountExtended } from 'helpers/vue_test_utils_helper'; -import { file } from '../helpers'; - -Vue.use(Vuex); - -const GlTabStub = stubComponent(GlTab, { - template: '`,
- codeEnd: `
`,
- helpStart: ``,
- helpEnd: '',
- },
- false,
- ),
- );
- });
-
- it('returns permission error, with status FORBIDDEN', () => {
- const result = messages.configCheckError(HTTP_STATUS_FORBIDDEN, TEST_HELP_URL);
-
- expect(result).toBe(messages.ERROR_PERMISSION);
- });
-
- it('returns unexpected error, with unexpected status', () => {
- const result = messages.configCheckError(HTTP_STATUS_NOT_FOUND, TEST_HELP_URL);
-
- expect(result).toBe(messages.UNEXPECTED_ERROR_CONFIG);
- });
- });
-});
diff --git a/spec/frontend/ide/stores/modules/terminal/mutations_spec.js b/spec/frontend/ide/stores/modules/terminal/mutations_spec.js
deleted file mode 100644
index 3451932a185..00000000000
--- a/spec/frontend/ide/stores/modules/terminal/mutations_spec.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import {
- CHECK_CONFIG,
- CHECK_RUNNERS,
- RUNNING,
- STOPPING,
-} from '~/ide/stores/modules/terminal/constants';
-import * as types from '~/ide/stores/modules/terminal/mutation_types';
-import mutations from '~/ide/stores/modules/terminal/mutations';
-import createState from '~/ide/stores/modules/terminal/state';
-
-describe('IDE store terminal mutations', () => {
- let state;
-
- beforeEach(() => {
- state = createState();
- });
-
- describe(types.SET_VISIBLE, () => {
- it('sets isVisible', () => {
- state.isVisible = false;
-
- mutations[types.SET_VISIBLE](state, true);
-
- expect(state.isVisible).toBe(true);
- });
- });
-
- describe(types.HIDE_SPLASH, () => {
- it('sets isShowSplash', () => {
- state.isShowSplash = true;
-
- mutations[types.HIDE_SPLASH](state);
-
- expect(state.isShowSplash).toBe(false);
- });
- });
-
- describe(types.SET_PATHS, () => {
- it('sets paths', () => {
- const paths = {
- test: 'foo',
- };
-
- mutations[types.SET_PATHS](state, paths);
-
- expect(state.paths).toBe(paths);
- });
- });
-
- describe(types.REQUEST_CHECK, () => {
- it('sets isLoading for check', () => {
- const type = CHECK_CONFIG;
-
- state.checks[type] = {};
- mutations[types.REQUEST_CHECK](state, type);
-
- expect(state.checks[type]).toEqual({
- isLoading: true,
- });
- });
- });
-
- describe(types.RECEIVE_CHECK_ERROR, () => {
- it('sets error for check', () => {
- const type = CHECK_RUNNERS;
- const message = 'lorem ipsum';
-
- state.checks[type] = {};
- mutations[types.RECEIVE_CHECK_ERROR](state, { type, message });
-
- expect(state.checks[type]).toEqual({
- isLoading: false,
- isValid: false,
- message,
- });
- });
- });
-
- describe(types.RECEIVE_CHECK_SUCCESS, () => {
- it('sets success for check', () => {
- const type = CHECK_CONFIG;
-
- state.checks[type] = {};
- mutations[types.RECEIVE_CHECK_SUCCESS](state, type);
-
- expect(state.checks[type]).toEqual({
- isLoading: false,
- isValid: true,
- message: null,
- });
- });
- });
-
- describe(types.SET_SESSION, () => {
- it('sets session', () => {
- const session = {
- terminalPath: 'terminal/foo',
- status: RUNNING,
- };
-
- mutations[types.SET_SESSION](state, session);
-
- expect(state.session).toBe(session);
- });
- });
-
- describe(types.SET_SESSION_STATUS, () => {
- it('sets session if a session does not exists', () => {
- const status = RUNNING;
-
- mutations[types.SET_SESSION_STATUS](state, status);
-
- expect(state.session).toEqual({
- status,
- });
- });
-
- it('sets session status', () => {
- state.session = {
- terminalPath: 'terminal/foo',
- status: RUNNING,
- };
-
- mutations[types.SET_SESSION_STATUS](state, STOPPING);
-
- expect(state.session).toEqual({
- terminalPath: 'terminal/foo',
- status: STOPPING,
- });
- });
- });
-
- describe(types.SET_SESSION_STATUS_INTERVAL, () => {
- it('sets sessionStatusInterval', () => {
- const val = 7;
-
- mutations[types.SET_SESSION_STATUS_INTERVAL](state, val);
-
- expect(state.sessionStatusInterval).toEqual(val);
- });
- });
-});
diff --git a/spec/frontend/ide/stores/modules/terminal_sync/actions_spec.js b/spec/frontend/ide/stores/modules/terminal_sync/actions_spec.js
deleted file mode 100644
index 448fd909f39..00000000000
--- a/spec/frontend/ide/stores/modules/terminal_sync/actions_spec.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import testAction from 'helpers/vuex_action_helper';
-import mirror, { canConnect, SERVICE_NAME } from '~/ide/lib/mirror';
-import * as actions from '~/ide/stores/modules/terminal_sync/actions';
-import * as types from '~/ide/stores/modules/terminal_sync/mutation_types';
-
-jest.mock('~/ide/lib/mirror');
-
-const TEST_SESSION = {
- proxyWebsocketPath: 'test/path',
- services: [SERVICE_NAME],
-};
-
-describe('ide/stores/modules/terminal_sync/actions', () => {
- let rootState;
-
- beforeEach(() => {
- canConnect.mockReturnValue(true);
- rootState = {
- changedFiles: [],
- terminal: {},
- };
- });
-
- describe('upload', () => {
- it('uploads to mirror and sets success', async () => {
- mirror.upload.mockReturnValue(Promise.resolve());
-
- await testAction(
- actions.upload,
- null,
- rootState,
- [{ type: types.START_LOADING }, { type: types.SET_SUCCESS }],
- [],
- );
- expect(mirror.upload).toHaveBeenCalledWith(rootState);
- });
-
- it('sets error when failed', () => {
- const err = { message: 'it failed!' };
- mirror.upload.mockReturnValue(Promise.reject(err));
-
- return testAction(
- actions.upload,
- null,
- rootState,
- [{ type: types.START_LOADING }, { type: types.SET_ERROR, payload: err }],
- [],
- );
- });
- });
-
- describe('stop', () => {
- it('disconnects from mirror', async () => {
- await testAction(actions.stop, null, rootState, [{ type: types.STOP }], []);
- expect(mirror.disconnect).toHaveBeenCalled();
- });
- });
-
- describe('start', () => {
- it.each`
- session | canConnectMock | description
- ${null} | ${true} | ${'does not exist'}
- ${{}} | ${true} | ${'does not have proxyWebsocketPath'}
- ${{ proxyWebsocketPath: 'test/path' }} | ${false} | ${'can not connect service'}
- `('rejects if session $description', ({ session, canConnectMock }) => {
- canConnect.mockReturnValue(canConnectMock);
-
- const result = actions.start({ rootState: { terminal: { session } } });
-
- return expect(result).rejects.toBe(undefined);
- });
-
- describe('with terminal session in state', () => {
- beforeEach(() => {
- rootState = {
- terminal: { session: TEST_SESSION },
- };
- });
-
- it('connects to mirror and sets success', async () => {
- mirror.connect.mockReturnValue(Promise.resolve());
-
- await testAction(
- actions.start,
- null,
- rootState,
- [{ type: types.START_LOADING }, { type: types.SET_SUCCESS }],
- [],
- );
- expect(mirror.connect).toHaveBeenCalledWith(TEST_SESSION.proxyWebsocketPath);
- });
-
- it('sets error if connection fails', () => {
- const commit = jest.fn();
- const err = new Error('test');
- mirror.connect.mockReturnValue(Promise.reject(err));
-
- const result = actions.start({ rootState, commit });
-
- return Promise.all([
- expect(result).rejects.toEqual(err),
- result.catch(() => {
- expect(commit).toHaveBeenCalledWith(types.SET_ERROR, err);
- }),
- ]);
- });
- });
- });
-});
diff --git a/spec/frontend/ide/stores/modules/terminal_sync/mutations_spec.js b/spec/frontend/ide/stores/modules/terminal_sync/mutations_spec.js
deleted file mode 100644
index b7dbf93f4e6..00000000000
--- a/spec/frontend/ide/stores/modules/terminal_sync/mutations_spec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import * as types from '~/ide/stores/modules/terminal_sync/mutation_types';
-import mutations from '~/ide/stores/modules/terminal_sync/mutations';
-import createState from '~/ide/stores/modules/terminal_sync/state';
-
-const TEST_MESSAGE = 'lorem ipsum dolar sit';
-
-describe('ide/stores/modules/terminal_sync/mutations', () => {
- let state;
-
- beforeEach(() => {
- state = createState();
- });
-
- describe(types.START_LOADING, () => {
- it('sets isLoading and resets error', () => {
- Object.assign(state, {
- isLoading: false,
- isError: true,
- });
-
- mutations[types.START_LOADING](state);
-
- expect(state).toEqual(
- expect.objectContaining({
- isLoading: true,
- isError: false,
- }),
- );
- });
- });
-
- describe(types.SET_ERROR, () => {
- it('sets isLoading and error message', () => {
- Object.assign(state, {
- isLoading: true,
- isError: false,
- message: '',
- });
-
- mutations[types.SET_ERROR](state, { message: TEST_MESSAGE });
-
- expect(state).toEqual(
- expect.objectContaining({
- isLoading: false,
- isError: true,
- message: TEST_MESSAGE,
- }),
- );
- });
- });
-
- describe(types.SET_SUCCESS, () => {
- it('sets isLoading and resets error and is started', () => {
- Object.assign(state, {
- isLoading: true,
- isError: true,
- isStarted: false,
- });
-
- mutations[types.SET_SUCCESS](state);
-
- expect(state).toEqual(
- expect.objectContaining({
- isLoading: false,
- isError: false,
- isStarted: true,
- }),
- );
- });
- });
-
- describe(types.STOP, () => {
- it('sets stop values', () => {
- Object.assign(state, {
- isLoading: true,
- isStarted: true,
- });
-
- mutations[types.STOP](state);
-
- expect(state).toEqual(
- expect.objectContaining({
- isLoading: false,
- isStarted: false,
- }),
- );
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations/branch_spec.js b/spec/frontend/ide/stores/mutations/branch_spec.js
deleted file mode 100644
index 30a688d2bb0..00000000000
--- a/spec/frontend/ide/stores/mutations/branch_spec.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import mutations from '~/ide/stores/mutations/branch';
-import state from '~/ide/stores/state';
-
-describe('Multi-file store branch mutations', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- describe('SET_CURRENT_BRANCH', () => {
- it('sets currentBranch', () => {
- mutations.SET_CURRENT_BRANCH(localState, 'main');
-
- expect(localState.currentBranchId).toBe('main');
- });
- });
-
- describe('SET_BRANCH_COMMIT', () => {
- it('sets the last commit on current project', () => {
- localState.projects = {
- Example: {
- branches: {
- main: {},
- },
- },
- };
-
- mutations.SET_BRANCH_COMMIT(localState, {
- projectId: 'Example',
- branchId: 'main',
- commit: {
- title: 'Example commit',
- },
- });
-
- expect(localState.projects.Example.branches.main.commit.title).toBe('Example commit');
- });
- });
-
- describe('SET_BRANCH_WORKING_REFERENCE', () => {
- beforeEach(() => {
- localState.projects = {
- Foo: {
- branches: {
- bar: {},
- },
- },
- };
- });
-
- it('sets workingReference for existing branch', () => {
- mutations.SET_BRANCH_WORKING_REFERENCE(localState, {
- projectId: 'Foo',
- branchId: 'bar',
- reference: 'foo-bar-ref',
- });
-
- expect(localState.projects.Foo.branches.bar.workingReference).toBe('foo-bar-ref');
- });
-
- it('does not fail on non-existent just yet branch', () => {
- expect(localState.projects.Foo.branches.unknown).toBeUndefined();
-
- mutations.SET_BRANCH_WORKING_REFERENCE(localState, {
- projectId: 'Foo',
- branchId: 'unknown',
- reference: 'fun-fun-ref',
- });
-
- expect(localState.projects.Foo.branches.unknown).not.toBeUndefined();
- expect(localState.projects.Foo.branches.unknown.workingReference).toBe('fun-fun-ref');
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations/file_spec.js b/spec/frontend/ide/stores/mutations/file_spec.js
deleted file mode 100644
index 69ec2e7a6f5..00000000000
--- a/spec/frontend/ide/stores/mutations/file_spec.js
+++ /dev/null
@@ -1,581 +0,0 @@
-import { createStore } from '~/ide/stores';
-import mutations from '~/ide/stores/mutations/file';
-import { file } from '../../helpers';
-
-describe('IDE store file mutations', () => {
- let localState;
- let localStore;
- let localFile;
-
- beforeEach(() => {
- localStore = createStore();
- localState = localStore.state;
- localFile = { ...file('file'), type: 'blob', content: 'original' };
-
- localState.entries[localFile.path] = localFile;
- });
-
- describe('SET_FILE_ACTIVE', () => {
- it('sets the file active', () => {
- mutations.SET_FILE_ACTIVE(localState, {
- path: localFile.path,
- active: true,
- });
-
- expect(localFile.active).toBe(true);
- });
-
- it('sets pending tab as not active', () => {
- localState.openFiles.push({ ...localFile, pending: true, active: true });
-
- mutations.SET_FILE_ACTIVE(localState, {
- path: localFile.path,
- active: true,
- });
-
- expect(localState.openFiles[0].active).toBe(false);
- });
- });
-
- describe('TOGGLE_FILE_OPEN', () => {
- it('adds into opened files', () => {
- mutations.TOGGLE_FILE_OPEN(localState, localFile.path);
-
- expect(localFile.opened).toBe(true);
- expect(localState.openFiles.length).toBe(1);
- });
-
- describe('if already open', () => {
- it('removes from opened files', () => {
- mutations.TOGGLE_FILE_OPEN(localState, localFile.path);
- mutations.TOGGLE_FILE_OPEN(localState, localFile.path);
-
- expect(localFile.opened).toBe(false);
- expect(localState.openFiles.length).toBe(0);
- });
- });
-
- it.each`
- entry | loading
- ${{ opened: false }} | ${true}
- ${{ opened: false, tempFile: true }} | ${false}
- ${{ opened: true }} | ${false}
- `('for state: $entry, sets loading=$loading', ({ entry, loading }) => {
- Object.assign(localFile, entry);
-
- mutations.TOGGLE_FILE_OPEN(localState, localFile.path);
-
- expect(localFile.loading).toBe(loading);
- });
- });
-
- describe('SET_FILE_DATA', () => {
- it('sets extra file data', () => {
- mutations.SET_FILE_DATA(localState, {
- data: {
- raw_path: 'raw',
- },
- file: localFile,
- });
-
- expect(localFile.rawPath).toBe('raw');
- expect(localFile.raw).toBeNull();
- expect(localFile.baseRaw).toBeNull();
- });
-
- it('sets extra file data to all arrays concerned', () => {
- localState.stagedFiles = [localFile];
- localState.changedFiles = [localFile];
- localState.openFiles = [localFile];
-
- const rawPath = 'foo/bar/blah.md';
-
- mutations.SET_FILE_DATA(localState, {
- data: {
- raw_path: rawPath,
- },
- file: localFile,
- });
-
- expect(localState.stagedFiles[0].rawPath).toEqual(rawPath);
- expect(localState.changedFiles[0].rawPath).toEqual(rawPath);
- expect(localState.openFiles[0].rawPath).toEqual(rawPath);
- expect(localFile.rawPath).toEqual(rawPath);
- });
-
- it('does not mutate certain props on the file', () => {
- const path = 'New Path';
- const name = 'New Name';
- localFile.path = path;
- localFile.name = name;
-
- localState.stagedFiles = [localFile];
- localState.changedFiles = [localFile];
- localState.openFiles = [localFile];
-
- mutations.SET_FILE_DATA(localState, {
- data: {
- path: 'Old Path',
- name: 'Old Name',
- raw: 'Old Raw',
- base_raw: 'Old Base Raw',
- },
- file: localFile,
- });
-
- [
- localState.stagedFiles[0],
- localState.changedFiles[0],
- localState.openFiles[0],
- localFile,
- ].forEach((f) => {
- expect(f).toEqual(
- expect.objectContaining({
- path,
- name,
- raw: null,
- baseRaw: null,
- }),
- );
- });
- });
- });
-
- describe('SET_FILE_RAW_DATA', () => {
- const callMutationForFile = (f) => {
- mutations.SET_FILE_RAW_DATA(localState, {
- file: f,
- raw: 'testing',
- fileDeletedAndReadded: localStore.getters.isFileDeletedAndReadded(localFile.path),
- });
- };
-
- it('sets raw data', () => {
- callMutationForFile(localFile);
-
- expect(localFile.raw).toBe('testing');
- });
-
- it('sets raw data to stagedFile if file was deleted and readded', () => {
- localState.stagedFiles = [{ ...localFile, deleted: true }];
- localFile.tempFile = true;
-
- callMutationForFile(localFile);
-
- expect(localFile.raw).toEqual('');
- expect(localState.stagedFiles[0].raw).toBe('testing');
- });
-
- it("sets raw data to a file's content if tempFile is empty", () => {
- localFile.tempFile = true;
- localFile.content = '';
-
- callMutationForFile(localFile);
-
- expect(localFile.raw).toEqual('');
- expect(localFile.content).toBe('testing');
- });
-
- it('adds raw data to open pending file', () => {
- localState.openFiles.push({ ...localFile, pending: true });
-
- callMutationForFile(localFile);
-
- expect(localState.openFiles[0].raw).toBe('testing');
- });
-
- it('sets raw to content of a renamed tempFile', () => {
- localFile.tempFile = true;
- localFile.prevPath = 'old_path';
- localState.openFiles.push({ ...localFile, pending: true });
-
- callMutationForFile(localFile);
-
- expect(localState.openFiles[0].raw).not.toBe('testing');
- expect(localState.openFiles[0].content).toBe('testing');
- });
-
- it('adds raw data to a staged deleted file if unstaged change has a tempFile of the same name', () => {
- localFile.tempFile = true;
- localState.openFiles.push({ ...localFile, pending: true });
- localState.stagedFiles = [{ ...localFile, deleted: true }];
-
- callMutationForFile(localFile);
-
- expect(localFile.raw).toEqual('');
- expect(localState.stagedFiles[0].raw).toBe('testing');
- });
- });
-
- describe('SET_FILE_BASE_RAW_DATA', () => {
- it('sets raw data from base branch', () => {
- mutations.SET_FILE_BASE_RAW_DATA(localState, {
- file: localFile,
- baseRaw: 'testing',
- });
-
- expect(localFile.baseRaw).toBe('testing');
- });
- });
-
- describe('UPDATE_FILE_CONTENT', () => {
- beforeEach(() => {
- localFile.raw = 'test';
- });
-
- it('sets content', () => {
- mutations.UPDATE_FILE_CONTENT(localState, {
- path: localFile.path,
- content: 'test',
- });
-
- expect(localFile.content).toBe('test');
- });
-
- it('sets changed if content does not match raw', () => {
- mutations.UPDATE_FILE_CONTENT(localState, {
- path: localFile.path,
- content: 'testing',
- });
-
- expect(localFile.content).toBe('testing');
- expect(localFile.changed).toBe(true);
- });
-
- it('sets changed if file is a temp file', () => {
- localFile.tempFile = true;
-
- mutations.UPDATE_FILE_CONTENT(localState, {
- path: localFile.path,
- content: '',
- });
-
- expect(localFile.changed).toBe(true);
- });
- });
-
- describe('SET_FILE_MERGE_REQUEST_CHANGE', () => {
- it('sets file mr change', () => {
- mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
- file: localFile,
- mrChange: {
- diff: 'ABC',
- },
- });
-
- expect(localFile.mrChange.diff).toBe('ABC');
- });
-
- it('has diffMode replaced by default', () => {
- mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
- file: localFile,
- mrChange: {
- diff: 'ABC',
- },
- });
-
- expect(localFile.mrChange.diffMode).toBe('replaced');
- });
-
- it('has diffMode new', () => {
- mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
- file: localFile,
- mrChange: {
- diff: 'ABC',
- new_file: true,
- },
- });
-
- expect(localFile.mrChange.diffMode).toBe('new');
- });
-
- it('has diffMode deleted', () => {
- mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
- file: localFile,
- mrChange: {
- diff: 'ABC',
- deleted_file: true,
- },
- });
-
- expect(localFile.mrChange.diffMode).toBe('deleted');
- });
-
- it('has diffMode renamed', () => {
- mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
- file: localFile,
- mrChange: {
- diff: 'ABC',
- renamed_file: true,
- },
- });
-
- expect(localFile.mrChange.diffMode).toBe('renamed');
- });
- });
-
- describe('DISCARD_FILE_CHANGES', () => {
- beforeEach(() => {
- localFile.content = 'test';
- localFile.changed = true;
- localState.currentProjectId = 'gitlab-ce';
- localState.currentBranchId = 'main';
- localState.trees['gitlab-ce/main'] = {
- tree: [],
- };
- });
-
- it('resets content and changed', () => {
- mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
-
- expect(localFile.content).toBe('');
- expect(localFile.changed).toBe(false);
- });
-
- it('adds to root tree if deleted', () => {
- localFile.deleted = true;
-
- mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
-
- expect(localState.trees['gitlab-ce/main'].tree).toEqual([{ ...localFile, deleted: false }]);
- });
-
- it('adds to parent tree if deleted', () => {
- localFile.deleted = true;
- localFile.parentPath = 'parentPath';
- localState.entries.parentPath = {
- tree: [],
- };
-
- mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
-
- expect(localState.entries.parentPath.tree).toEqual([{ ...localFile, deleted: false }]);
- });
- });
-
- describe('ADD_FILE_TO_CHANGED', () => {
- it('adds file into changed files array', () => {
- mutations.ADD_FILE_TO_CHANGED(localState, localFile.path);
-
- expect(localState.changedFiles.length).toBe(1);
- });
- });
-
- describe('REMOVE_FILE_FROM_CHANGED', () => {
- it('removes files from changed files array', () => {
- localState.changedFiles.push(localFile);
-
- mutations.REMOVE_FILE_FROM_CHANGED(localState, localFile.path);
-
- expect(localState.changedFiles.length).toBe(0);
- });
- });
-
- describe.each`
- mutationName | mutation | addedTo | removedFrom | staged | changedFilesCount | stagedFilesCount
- ${'STAGE_CHANGE'} | ${mutations.STAGE_CHANGE} | ${'stagedFiles'} | ${'changedFiles'} | ${true} | ${0} | ${1}
- ${'UNSTAGE_CHANGE'} | ${mutations.UNSTAGE_CHANGE} | ${'changedFiles'} | ${'stagedFiles'} | ${false} | ${1} | ${0}
- `(
- '$mutationName',
- ({ mutation, changedFilesCount, removedFrom, addedTo, staged, stagedFilesCount }) => {
- let unstagedFile;
- let stagedFile;
-
- beforeEach(() => {
- unstagedFile = {
- ...file('file'),
- type: 'blob',
- raw: 'original content',
- content: 'changed content',
- };
-
- stagedFile = {
- ...unstagedFile,
- content: 'staged content',
- staged: true,
- };
-
- localState.changedFiles.push(unstagedFile);
- localState.stagedFiles.push(stagedFile);
- localState.entries[unstagedFile.path] = unstagedFile;
- });
-
- it('removes all changes of a file if staged and unstaged change contents are equal', () => {
- unstagedFile.content = 'original content';
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.entries.file).toEqual(
- expect.objectContaining({
- content: 'original content',
- staged: false,
- changed: false,
- }),
- );
-
- expect(localState.stagedFiles.length).toBe(0);
- expect(localState.changedFiles.length).toBe(0);
- });
-
- it('removes all changes of a file if a file is deleted and a new file with same content is added', () => {
- stagedFile.deleted = true;
- unstagedFile.tempFile = true;
- unstagedFile.content = 'original content';
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.stagedFiles.length).toBe(0);
- expect(localState.changedFiles.length).toBe(0);
-
- expect(localState.entries.file).toEqual(
- expect.objectContaining({
- content: 'original content',
- deleted: false,
- tempFile: false,
- }),
- );
- });
-
- it('merges deleted and added file into a changed file if the contents differ', () => {
- stagedFile.deleted = true;
- unstagedFile.tempFile = true;
- unstagedFile.content = 'hello';
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.stagedFiles.length).toBe(stagedFilesCount);
- expect(localState.changedFiles.length).toBe(changedFilesCount);
-
- expect(unstagedFile).toEqual(
- expect.objectContaining({
- content: 'hello',
- staged,
- deleted: false,
- tempFile: false,
- changed: true,
- }),
- );
- });
-
- it('does not remove file from stagedFiles and changedFiles if the file was renamed, even if the contents are equal', () => {
- unstagedFile.content = 'original content';
- unstagedFile.prevPath = 'old_file';
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.entries.file).toEqual(
- expect.objectContaining({
- content: 'original content',
- staged,
- changed: false,
- prevPath: 'old_file',
- }),
- );
-
- expect(localState.stagedFiles.length).toBe(stagedFilesCount);
- expect(localState.changedFiles.length).toBe(changedFilesCount);
- });
-
- it(`removes file from ${removedFrom} array and adds it into ${addedTo} array`, () => {
- localState.stagedFiles.length = 0;
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.stagedFiles.length).toBe(stagedFilesCount);
- expect(localState.changedFiles.length).toBe(changedFilesCount);
-
- const f = localState.stagedFiles[0] || localState.changedFiles[0];
- expect(f).toEqual(unstagedFile);
- });
-
- it(`updates file in ${addedTo} array if it is was already present in it`, () => {
- unstagedFile.raw = 'testing 123';
-
- mutation(localState, {
- path: unstagedFile.path,
- diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
- });
-
- expect(localState.stagedFiles.length).toBe(stagedFilesCount);
- expect(localState.changedFiles.length).toBe(changedFilesCount);
-
- const f = localState.stagedFiles[0] || localState.changedFiles[0];
- expect(f.raw).toEqual('testing 123');
- });
- },
- );
-
- describe('TOGGLE_FILE_CHANGED', () => {
- it('updates file changed status', () => {
- mutations.TOGGLE_FILE_CHANGED(localState, {
- file: localFile,
- changed: true,
- });
-
- expect(localFile.changed).toBe(true);
- });
- });
-
- describe('ADD_PENDING_TAB', () => {
- beforeEach(() => {
- const f = { ...file('openFile'), path: 'openFile', active: true, opened: true };
-
- localState.entries[f.path] = f;
- localState.openFiles.push(f);
- });
-
- it('adds file into openFiles as pending', () => {
- mutations.ADD_PENDING_TAB(localState, {
- file: localFile,
- });
-
- expect(localState.openFiles.length).toBe(1);
- expect(localState.openFiles[0].pending).toBe(true);
- expect(localState.openFiles[0].key).toBe(`pending-${localFile.key}`);
- });
-
- it('only allows 1 open pending file', () => {
- const newFile = file('test');
- localState.entries[newFile.path] = newFile;
-
- mutations.ADD_PENDING_TAB(localState, {
- file: localFile,
- });
-
- expect(localState.openFiles.length).toBe(1);
-
- mutations.ADD_PENDING_TAB(localState, {
- file: file('test'),
- });
-
- expect(localState.openFiles.length).toBe(1);
- expect(localState.openFiles[0].name).toBe('test');
- });
- });
-
- describe('REMOVE_PENDING_TAB', () => {
- it('removes pending tab from openFiles', () => {
- localFile.key = 'testing';
- localState.openFiles.push(localFile);
-
- mutations.REMOVE_PENDING_TAB(localState, localFile);
-
- expect(localState.openFiles.length).toBe(0);
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations/merge_request_spec.js b/spec/frontend/ide/stores/mutations/merge_request_spec.js
deleted file mode 100644
index 2af06835181..00000000000
--- a/spec/frontend/ide/stores/mutations/merge_request_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import mutations from '~/ide/stores/mutations/merge_request';
-import state from '~/ide/stores/state';
-
-describe('IDE store merge request mutations', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- localState.projects = { abcproject: { mergeRequests: {} } };
-
- mutations.SET_MERGE_REQUEST(localState, {
- projectPath: 'abcproject',
- mergeRequestId: 1,
- mergeRequest: {
- title: 'mr',
- },
- });
- });
-
- describe('SET_CURRENT_MERGE_REQUEST', () => {
- it('sets current merge request', () => {
- mutations.SET_CURRENT_MERGE_REQUEST(localState, 2);
-
- expect(localState.currentMergeRequestId).toBe(2);
- });
- });
-
- describe('SET_MERGE_REQUEST', () => {
- it('setsmerge request data', () => {
- const newMr = localState.projects.abcproject.mergeRequests[1];
-
- expect(newMr.title).toBe('mr');
- expect(newMr.active).toBe(true);
- });
-
- it('keeps original data', () => {
- const versions = ['change'];
- const mergeRequest = localState.projects.abcproject.mergeRequests[1];
-
- mergeRequest.versions = versions;
-
- mutations.SET_MERGE_REQUEST(localState, {
- projectPath: 'abcproject',
- mergeRequestId: 1,
- mergeRequest: {
- title: ['change'],
- },
- });
-
- expect(mergeRequest.title).toBe('mr');
- expect(mergeRequest.versions).toEqual(versions);
- });
- });
-
- describe('SET_MERGE_REQUEST_CHANGES', () => {
- it('sets merge request changes', () => {
- mutations.SET_MERGE_REQUEST_CHANGES(localState, {
- projectPath: 'abcproject',
- mergeRequestId: 1,
- changes: {
- diff: 'abc',
- },
- });
-
- const newMr = localState.projects.abcproject.mergeRequests[1];
-
- expect(newMr.changes.diff).toBe('abc');
- });
- });
-
- describe('SET_MERGE_REQUEST_VERSIONS', () => {
- it('sets merge request versions', () => {
- mutations.SET_MERGE_REQUEST_VERSIONS(localState, {
- projectPath: 'abcproject',
- mergeRequestId: 1,
- versions: [{ id: 123 }],
- });
-
- const newMr = localState.projects.abcproject.mergeRequests[1];
-
- expect(newMr.versions.length).toBe(1);
- expect(newMr.versions[0].id).toBe(123);
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations/project_spec.js b/spec/frontend/ide/stores/mutations/project_spec.js
deleted file mode 100644
index 0fdd7798f00..00000000000
--- a/spec/frontend/ide/stores/mutations/project_spec.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import mutations from '~/ide/stores/mutations/project';
-import state from '~/ide/stores/state';
-
-describe('Multi-file store branch mutations', () => {
- let localState;
- const nonExistentProj = 'nonexistent';
- const existingProj = 'abcproject';
-
- beforeEach(() => {
- localState = state();
- localState.projects = { [existingProj]: { empty_repo: true } };
- });
-
- describe('TOGGLE_EMPTY_STATE', () => {
- it('sets empty_repo for project to passed value', () => {
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: false });
-
- expect(localState.projects[existingProj].empty_repo).toBe(false);
-
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: true });
-
- expect(localState.projects[existingProj].empty_repo).toBe(true);
- });
- });
-
- describe('UPDATE_PROJECT', () => {
- it.each`
- desc | projectPath | props | expectedProps
- ${'extends existing project with the passed props'} | ${existingProj} | ${{ foo1: 'bar' }} | ${{ foo1: 'bar' }}
- ${'overrides existing props on the exsiting project'} | ${existingProj} | ${{ empty_repo: false }} | ${{ empty_repo: false }}
- ${'does nothing if the project does not exist'} | ${nonExistentProj} | ${{ foo2: 'bar' }} | ${undefined}
- ${'does nothing if project is not passed'} | ${undefined} | ${{ foo3: 'bar' }} | ${undefined}
- ${'does nothing if the props are not passed'} | ${existingProj} | ${undefined} | ${{}}
- ${'does nothing if the props are empty'} | ${existingProj} | ${{}} | ${{}}
- `('$desc', ({ projectPath, props, expectedProps } = {}) => {
- const origProject = localState.projects[projectPath];
-
- mutations.UPDATE_PROJECT(localState, { projectPath, props });
-
- if (!expectedProps) {
- expect(localState.projects[projectPath]).toBeUndefined();
- } else {
- expect(localState.projects[projectPath]).toEqual({
- ...origProject,
- ...expectedProps,
- });
- }
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations/tree_spec.js b/spec/frontend/ide/stores/mutations/tree_spec.js
deleted file mode 100644
index a8c0d7ba2c8..00000000000
--- a/spec/frontend/ide/stores/mutations/tree_spec.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import mutations from '~/ide/stores/mutations/tree';
-import state from '~/ide/stores/state';
-import { file } from '../../helpers';
-
-describe('Multi-file store tree mutations', () => {
- let localState;
- let localTree;
-
- beforeEach(() => {
- localState = state();
- localTree = file();
-
- localState.entries[localTree.path] = localTree;
- });
-
- describe('TOGGLE_TREE_OPEN', () => {
- it('toggles tree open', () => {
- mutations.TOGGLE_TREE_OPEN(localState, localTree.path);
-
- expect(localTree.opened).toBe(true);
-
- mutations.TOGGLE_TREE_OPEN(localState, localTree.path);
-
- expect(localTree.opened).toBe(false);
- });
- });
-
- describe('SET_DIRECTORY_DATA', () => {
- let data;
-
- beforeEach(() => {
- data = [file('tree'), file('foo'), file('blob')];
- });
-
- it('adds directory data', () => {
- localState.trees['project/main'] = {
- tree: [],
- };
-
- mutations.SET_DIRECTORY_DATA(localState, {
- data,
- treePath: 'project/main',
- });
-
- const tree = localState.trees['project/main'];
-
- expect(tree.tree.length).toBe(3);
- expect(tree.tree[0].name).toBe('tree');
- expect(tree.tree[1].name).toBe('foo');
- expect(tree.tree[2].name).toBe('blob');
- });
-
- it('keeps loading state', () => {
- mutations.CREATE_TREE(localState, {
- treePath: 'project/main',
- });
- mutations.SET_DIRECTORY_DATA(localState, {
- data,
- treePath: 'project/main',
- });
-
- expect(localState.trees['project/main'].loading).toBe(true);
- });
-
- it('does not override tree already in state, but merges the two with correct order', () => {
- const openedFile = file('new');
-
- localState.trees['project/main'] = {
- loading: true,
- tree: [openedFile],
- };
-
- mutations.SET_DIRECTORY_DATA(localState, {
- data,
- treePath: 'project/main',
- });
-
- const { tree } = localState.trees['project/main'];
-
- expect(tree.length).toBe(4);
- expect(tree[0].name).toBe('blob');
- expect(tree[1].name).toBe('foo');
- expect(tree[2].name).toBe('new');
- expect(tree[3].name).toBe('tree');
- });
-
- it('returns tree unchanged if the opened file is already in the tree', () => {
- const openedFile = file('foo');
- localState.trees['project/main'] = {
- loading: true,
- tree: [openedFile],
- };
-
- mutations.SET_DIRECTORY_DATA(localState, {
- data,
- treePath: 'project/main',
- });
-
- const { tree } = localState.trees['project/main'];
-
- expect(tree.length).toBe(3);
-
- expect(tree[0].name).toBe('tree');
- expect(tree[1].name).toBe('foo');
- expect(tree[2].name).toBe('blob');
- });
- });
-
- describe('REMOVE_ALL_CHANGES_FILES', () => {
- it('removes all files from changedFiles state', () => {
- localState.changedFiles.push(file('REMOVE_ALL_CHANGES_FILES'));
-
- mutations.REMOVE_ALL_CHANGES_FILES(localState);
-
- expect(localState.changedFiles.length).toBe(0);
- });
- });
-});
diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js
deleted file mode 100644
index ae21d257bb2..00000000000
--- a/spec/frontend/ide/stores/mutations_spec.js
+++ /dev/null
@@ -1,660 +0,0 @@
-import { TEST_HOST } from 'helpers/test_constants';
-import mutations from '~/ide/stores/mutations';
-import state from '~/ide/stores/state';
-import { file } from '../helpers';
-
-describe('Multi-file store mutations', () => {
- let localState;
- let entry;
-
- beforeEach(() => {
- localState = state();
- entry = file();
-
- localState.entries[entry.path] = entry;
- });
-
- describe('SET_INITIAL_DATA', () => {
- it('sets all initial data', () => {
- mutations.SET_INITIAL_DATA(localState, {
- test: 'test',
- });
-
- expect(localState.test).toBe('test');
- });
- });
-
- describe('TOGGLE_LOADING', () => {
- it('toggles loading of entry', () => {
- mutations.TOGGLE_LOADING(localState, {
- entry,
- });
-
- expect(entry.loading).toBe(true);
-
- mutations.TOGGLE_LOADING(localState, {
- entry,
- });
-
- expect(entry.loading).toBe(false);
- });
-
- it('toggles loading of entry and sets specific value', () => {
- mutations.TOGGLE_LOADING(localState, {
- entry,
- });
-
- expect(entry.loading).toBe(true);
-
- mutations.TOGGLE_LOADING(localState, {
- entry,
- forceValue: true,
- });
-
- expect(entry.loading).toBe(true);
- });
- });
-
- describe('CLEAR_STAGED_CHANGES', () => {
- it('clears stagedFiles array', () => {
- localState.stagedFiles.push('a');
-
- mutations.CLEAR_STAGED_CHANGES(localState);
-
- expect(localState.stagedFiles.length).toBe(0);
- });
- });
-
- describe('UPDATE_VIEWER', () => {
- it('sets viewer state', () => {
- mutations.UPDATE_VIEWER(localState, 'diff');
-
- expect(localState.viewer).toBe('diff');
- });
- });
-
- describe('UPDATE_ACTIVITY_BAR_VIEW', () => {
- it('updates currentActivityBar', () => {
- mutations.UPDATE_ACTIVITY_BAR_VIEW(localState, 'test');
-
- expect(localState.currentActivityView).toBe('test');
- });
- });
-
- describe('SET_EMPTY_STATE_SVGS', () => {
- it('updates empty state SVGs', () => {
- mutations.SET_EMPTY_STATE_SVGS(localState, {
- 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');
- });
- });
-
- describe('CREATE_TMP_ENTRY', () => {
- beforeEach(() => {
- localState.currentProjectId = 'gitlab-ce';
- localState.currentBranchId = 'main';
- localState.trees['gitlab-ce/main'] = {
- tree: [],
- };
- });
-
- it('creates temp entry in the tree', () => {
- const tmpFile = file('test');
- mutations.CREATE_TMP_ENTRY(localState, {
- data: {
- entries: {
- test: { ...tmpFile, tempFile: true, changed: true },
- },
- treeList: [tmpFile],
- },
- });
-
- expect(localState.trees['gitlab-ce/main'].tree.length).toEqual(1);
- expect(localState.entries.test.tempFile).toEqual(true);
- });
- });
-
- describe('UPDATE_TEMP_FLAG', () => {
- beforeEach(() => {
- localState.entries.test = { ...file(), tempFile: true, changed: true };
- });
-
- it('updates tempFile flag', () => {
- mutations.UPDATE_TEMP_FLAG(localState, {
- path: 'test',
- tempFile: false,
- });
-
- expect(localState.entries.test.tempFile).toBe(false);
- });
-
- it('updates changed flag', () => {
- mutations.UPDATE_TEMP_FLAG(localState, {
- path: 'test',
- tempFile: false,
- });
-
- expect(localState.entries.test.changed).toBe(false);
- });
- });
-
- describe('TOGGLE_FILE_FINDER', () => {
- it('updates fileFindVisible', () => {
- mutations.TOGGLE_FILE_FINDER(localState, true);
-
- expect(localState.fileFindVisible).toBe(true);
- });
- });
-
- describe('SET_ERROR_MESSAGE', () => {
- it('updates error message', () => {
- mutations.SET_ERROR_MESSAGE(localState, 'error');
-
- expect(localState.errorMessage).toBe('error');
- });
- });
-
- describe('DELETE_ENTRY', () => {
- beforeEach(() => {
- localState.currentProjectId = 'gitlab-ce';
- localState.currentBranchId = 'main';
- localState.trees['gitlab-ce/main'] = {
- tree: [],
- };
- });
-
- it('sets deleted flag', () => {
- localState.entries.filePath = {
- deleted: false,
- };
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.entries.filePath.deleted).toBe(true);
- });
-
- it('removes from root tree', () => {
- localState.entries.filePath = {
- path: 'filePath',
- deleted: false,
- };
- localState.trees['gitlab-ce/main'].tree.push(localState.entries.filePath);
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.trees['gitlab-ce/main'].tree).toEqual([]);
- });
-
- it('removes from parent tree', () => {
- localState.entries.filePath = {
- path: 'filePath',
- deleted: false,
- parentPath: 'parentPath',
- };
- localState.entries.parentPath = {
- tree: [localState.entries.filePath],
- };
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.entries.parentPath.tree).toEqual([]);
- });
-
- it('adds to changedFiles', () => {
- localState.entries.filePath = {
- deleted: false,
- type: 'blob',
- };
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.changedFiles).toEqual([localState.entries.filePath]);
- });
-
- it('does not add tempFile into changedFiles', () => {
- localState.entries.filePath = {
- deleted: false,
- type: 'blob',
- tempFile: true,
- };
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.changedFiles).toEqual([]);
- });
-
- it('removes tempFile from changedFiles and stagedFiles when deleted', () => {
- localState.entries.filePath = {
- path: 'filePath',
- deleted: false,
- type: 'blob',
- tempFile: true,
- };
-
- localState.changedFiles.push({ ...localState.entries.filePath });
- localState.stagedFiles.push({ ...localState.entries.filePath });
-
- mutations.DELETE_ENTRY(localState, 'filePath');
-
- expect(localState.changedFiles).toEqual([]);
- expect(localState.stagedFiles).toEqual([]);
- });
- });
-
- describe('UPDATE_FILE_AFTER_COMMIT', () => {
- it('updates URLs if prevPath is set', () => {
- const f = {
- ...file('test'),
- prevPath: 'testing-123',
- rawPath: `${TEST_HOST}/testing-123`,
- };
- localState.entries.test = f;
- localState.changedFiles.push(f);
-
- mutations.UPDATE_FILE_AFTER_COMMIT(localState, {
- file: f,
- lastCommit: {
- commit: {},
- },
- });
-
- expect(f).toEqual(
- expect.objectContaining({
- rawPath: `${TEST_HOST}/test`,
- prevId: undefined,
- prevPath: undefined,
- prevName: undefined,
- prevKey: undefined,
- }),
- );
- });
- });
-
- describe('RENAME_ENTRY', () => {
- beforeEach(() => {
- localState.trees = {
- 'gitlab-ce/main': {
- tree: [],
- },
- };
- localState.currentProjectId = 'gitlab-ce';
- localState.currentBranchId = 'main';
- localState.entries = {
- oldPath: file('oldPath', 'oldPath', 'blob'),
- };
- });
-
- it('updates existing entry without creating a new one', () => {
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- parentPath: '',
- });
-
- expect(localState.entries).toEqual({
- newPath: expect.objectContaining({
- path: 'newPath',
- prevPath: 'oldPath',
- }),
- });
- });
-
- it('correctly handles consecutive renames for the same entry', () => {
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- parentPath: '',
- });
-
- mutations.RENAME_ENTRY(localState, {
- path: 'newPath',
- name: 'newestPath',
- parentPath: '',
- });
-
- expect(localState.entries).toEqual({
- newestPath: expect.objectContaining({
- path: 'newestPath',
- prevPath: 'oldPath',
- }),
- });
- });
-
- it('correctly handles the same entry within a consecutively renamed folder', () => {
- const oldPath = file('root-folder/oldPath', 'root-folder/oldPath', 'blob');
- localState.entries = {
- 'root-folder': { ...file('root-folder', 'root-folder', 'tree'), tree: [oldPath] },
- 'root-folder/oldPath': oldPath,
- };
- Object.assign(localState.entries['root-folder/oldPath'], {
- parentPath: 'root-folder',
- });
-
- mutations.RENAME_ENTRY(localState, {
- path: 'root-folder/oldPath',
- name: 'renamed-folder/oldPath',
- entryPath: null,
- parentPath: '',
- });
-
- mutations.RENAME_ENTRY(localState, {
- path: 'renamed-folder/oldPath',
- name: 'simply-renamed/oldPath',
- entryPath: null,
- parentPath: '',
- });
-
- expect(localState.entries).toEqual({
- 'root-folder': expect.objectContaining({
- path: 'root-folder',
- }),
- 'simply-renamed/oldPath': expect.objectContaining({
- path: 'simply-renamed/oldPath',
- prevPath: 'root-folder/oldPath',
- }),
- });
- });
-
- it('renames entry, preserving old parameters', () => {
- const oldPathData = localState.entries.oldPath;
-
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- parentPath: '',
- });
-
- expect(localState.entries.newPath).toEqual({
- ...oldPathData,
- id: 'newPath',
- path: 'newPath',
- name: 'newPath',
- key: expect.stringMatching('newPath'),
- prevId: 'oldPath',
- prevName: 'oldPath',
- prevPath: 'oldPath',
- prevKey: oldPathData.key,
- prevParentPath: oldPathData.parentPath,
- });
- });
-
- it('does not store previous attributes on temp files', () => {
- Object.assign(localState.entries.oldPath, {
- tempFile: true,
- });
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- entryPath: null,
- parentPath: '',
- });
-
- expect(localState.entries.newPath).not.toEqual(
- expect.objectContaining({
- prevId: expect.anything(),
- prevName: expect.anything(),
- prevPath: expect.anything(),
- prevKey: expect.anything(),
- prevParentPath: expect.anything(),
- }),
- );
- });
-
- it('properly handles files with spaces in name', () => {
- const path = 'my fancy path';
- const newPath = 'new path';
- const oldEntry = file(path, path, 'blob');
-
- localState.entries[path] = oldEntry;
-
- mutations.RENAME_ENTRY(localState, {
- path,
- name: newPath,
- entryPath: null,
- parentPath: '',
- });
-
- expect(localState.entries[newPath]).toEqual({
- ...oldEntry,
- id: newPath,
- path: newPath,
- name: newPath,
- key: expect.stringMatching(newPath),
- prevId: path,
- prevName: path,
- prevPath: path,
- prevKey: oldEntry.key,
- prevParentPath: oldEntry.parentPath,
- });
- });
-
- it('adds to parent tree', () => {
- const parentEntry = {
- ...file('parentPath', 'parentPath', 'tree'),
- tree: [localState.entries.oldPath],
- };
- localState.entries.parentPath = parentEntry;
-
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- entryPath: null,
- parentPath: 'parentPath',
- });
-
- expect(parentEntry.tree.length).toBe(1);
- expect(parentEntry.tree[0].name).toBe('newPath');
- });
-
- it('sorts tree after renaming an entry', () => {
- const alpha = file('alpha', 'alpha', 'blob');
- const beta = file('beta', 'beta', 'blob');
- const gamma = file('gamma', 'gamma', 'blob');
- localState.entries = {
- alpha,
- beta,
- gamma,
- };
-
- localState.trees['gitlab-ce/main'].tree = [alpha, beta, gamma];
-
- mutations.RENAME_ENTRY(localState, {
- path: 'alpha',
- name: 'theta',
- entryPath: null,
- parentPath: '',
- });
-
- expect(localState.trees['gitlab-ce/main'].tree).toEqual([
- expect.objectContaining({
- name: 'beta',
- }),
- expect.objectContaining({
- name: 'gamma',
- }),
- expect.objectContaining({
- path: 'theta',
- name: 'theta',
- }),
- ]);
- });
-
- it('updates openFiles with the renamed one if the original one is open', () => {
- Object.assign(localState.entries.oldPath, {
- opened: true,
- type: 'blob',
- });
- Object.assign(localState, {
- openFiles: [localState.entries.oldPath],
- });
-
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- });
-
- expect(localState.openFiles.length).toBe(1);
- expect(localState.openFiles[0].path).toBe('newPath');
- });
-
- it('does not add renamed entry to changedFiles', () => {
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- });
-
- expect(localState.changedFiles.length).toBe(0);
- });
-
- it('updates existing changedFiles entry with the renamed one', () => {
- const origFile = { ...file('oldPath', 'oldPath', 'blob'), content: 'Foo' };
-
- Object.assign(localState, {
- changedFiles: [origFile],
- });
- Object.assign(localState.entries, {
- oldPath: origFile,
- });
-
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- });
-
- expect(localState.changedFiles).toEqual([
- expect.objectContaining({
- path: 'newPath',
- content: 'Foo',
- }),
- ]);
- });
-
- it('correctly saves original values if an entry is renamed multiple times', () => {
- const original = { ...localState.entries.oldPath };
- const paramsToCheck = ['prevId', 'prevPath', 'prevName'];
- const expectedObj = paramsToCheck.reduce(
- (o, param) => ({ ...o, [param]: original[param.replace('prev', '').toLowerCase()] }),
- {},
- );
-
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath',
- });
-
- expect(localState.entries.newPath).toEqual(expect.objectContaining(expectedObj));
-
- mutations.RENAME_ENTRY(localState, {
- path: 'newPath',
- name: 'newer',
- });
-
- expect(localState.entries.newer).toEqual(expect.objectContaining(expectedObj));
- });
-
- describe('renaming back to original', () => {
- beforeEach(() => {
- const renamedEntry = {
- ...file('renamed', 'renamed', 'blob'),
- prevId: 'lorem/orig',
- prevPath: 'lorem/orig',
- prevName: 'orig',
- prevKey: 'lorem/orig',
- prevParentPath: 'lorem',
- };
-
- localState.entries = {
- renamed: renamedEntry,
- };
-
- mutations.RENAME_ENTRY(localState, {
- path: 'renamed',
- name: 'orig',
- parentPath: 'lorem',
- });
- });
-
- it('renames entry and clears prev properties', () => {
- expect(localState.entries).toEqual({
- 'lorem/orig': expect.objectContaining({
- id: 'lorem/orig',
- path: 'lorem/orig',
- name: 'orig',
- prevId: undefined,
- prevPath: undefined,
- prevName: undefined,
- prevKey: undefined,
- prevParentPath: undefined,
- }),
- });
- });
- });
-
- describe('key updates', () => {
- beforeEach(() => {
- const rootFolder = file('rootFolder', 'rootFolder', 'tree');
- localState.entries = {
- rootFolder,
- oldPath: file('oldPath', 'oldPath', 'blob'),
- 'oldPath.txt': file('oldPath.txt', 'oldPath.txt', 'blob'),
- 'rootFolder/oldPath.md': file('oldPath.md', 'oldPath.md', 'blob', rootFolder),
- };
- });
-
- it('sets properly constucted key while preserving the original one', () => {
- const key = 'oldPath.txt-blob-oldPath.txt';
- localState.entries['oldPath.txt'].key = key;
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath.txt',
- name: 'newPath.md',
- });
-
- expect(localState.entries['newPath.md'].key).toBe('newPath.md-blob-newPath.md');
- expect(localState.entries['newPath.md'].prevKey).toBe(key);
- });
-
- it('correctly updates key for an entry without an extension', () => {
- localState.entries.oldPath.key = 'oldPath-blob-oldPath';
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath',
- name: 'newPath.md',
- });
-
- expect(localState.entries['newPath.md'].key).toBe('newPath.md-blob-newPath.md');
- });
-
- it('correctly updates key when new name does not have an extension', () => {
- localState.entries['oldPath.txt'].key = 'oldPath.txt-blob-oldPath.txt';
- mutations.RENAME_ENTRY(localState, {
- path: 'oldPath.txt',
- name: 'newPath',
- });
-
- expect(localState.entries.newPath.key).toBe('newPath-blob-newPath');
- });
-
- it('correctly updates key when renaming an entry in a folder', () => {
- localState.entries['rootFolder/oldPath.md'].key =
- 'rootFolder/oldPath.md-blob-rootFolder/oldPath.md';
- mutations.RENAME_ENTRY(localState, {
- path: 'rootFolder/oldPath.md',
- name: 'newPath.md',
- entryPath: null,
- parentPath: 'rootFolder',
- });
-
- expect(localState.entries['rootFolder/newPath.md'].key).toBe(
- 'rootFolder/newPath.md-blob-rootFolder/newPath.md',
- );
- });
- });
- });
-});
diff --git a/spec/frontend/ide/stores/plugins/terminal_spec.js b/spec/frontend/ide/stores/plugins/terminal_spec.js
deleted file mode 100644
index b2d5d85e005..00000000000
--- a/spec/frontend/ide/stores/plugins/terminal_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import Vue from 'vue';
-// eslint-disable-next-line no-restricted-imports
-import Vuex from 'vuex';
-import { TEST_HOST } from 'helpers/test_constants';
-import terminalModule from '~/ide/stores/modules/terminal';
-import { SET_BRANCH_WORKING_REFERENCE } from '~/ide/stores/mutation_types';
-import createTerminalPlugin from '~/ide/stores/plugins/terminal';
-
-const TEST_DATASET = {
- webTerminalSvgPath: `${TEST_HOST}/web/terminal/svg`,
- webTerminalHelpPath: `${TEST_HOST}/web/terminal/help`,
- webTerminalConfigHelpPath: `${TEST_HOST}/web/terminal/config/help`,
- webTerminalRunnersHelpPath: `${TEST_HOST}/web/terminal/runners/help`,
-};
-Vue.use(Vuex);
-
-describe('ide/stores/extend', () => {
- let store;
-
- beforeEach(() => {
- const el = document.createElement('div');
- Object.assign(el.dataset, TEST_DATASET);
-
- store = new Vuex.Store({
- mutations: {
- [SET_BRANCH_WORKING_REFERENCE]: () => {},
- },
- });
-
- jest.spyOn(store, 'registerModule').mockImplementation();
- jest.spyOn(store, 'dispatch').mockImplementation();
-
- const plugin = createTerminalPlugin(el);
-
- plugin(store);
- });
-
- it('registers terminal module', () => {
- expect(store.registerModule).toHaveBeenCalledWith('terminal', terminalModule());
- });
-
- it('dispatches terminal/setPaths', () => {
- expect(store.dispatch).toHaveBeenCalledWith('terminal/setPaths', {
- webTerminalSvgPath: TEST_DATASET.webTerminalSvgPath,
- webTerminalHelpPath: TEST_DATASET.webTerminalHelpPath,
- webTerminalConfigHelpPath: TEST_DATASET.webTerminalConfigHelpPath,
- webTerminalRunnersHelpPath: TEST_DATASET.webTerminalRunnersHelpPath,
- });
- });
-
- it(`dispatches terminal/init on ${SET_BRANCH_WORKING_REFERENCE}`, () => {
- store.dispatch.mockReset();
-
- store.commit(SET_BRANCH_WORKING_REFERENCE);
-
- expect(store.dispatch).toHaveBeenCalledWith('terminal/init');
- });
-});
diff --git a/spec/frontend/ide/stores/plugins/terminal_sync_spec.js b/spec/frontend/ide/stores/plugins/terminal_sync_spec.js
deleted file mode 100644
index f12f80c1602..00000000000
--- a/spec/frontend/ide/stores/plugins/terminal_sync_spec.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import eventHub from '~/ide/eventhub';
-import { createStore } from '~/ide/stores';
-import { RUNNING, STOPPING } from '~/ide/stores/modules/terminal/constants';
-import { SET_SESSION_STATUS } from '~/ide/stores/modules/terminal/mutation_types';
-import createTerminalPlugin from '~/ide/stores/plugins/terminal';
-import createTerminalSyncPlugin from '~/ide/stores/plugins/terminal_sync';
-import { createTriggerUpdatePayload } from '../../helpers';
-
-jest.mock('~/ide/lib/mirror');
-
-const ACTION_START = 'terminalSync/start';
-const ACTION_STOP = 'terminalSync/stop';
-const ACTION_UPLOAD = 'terminalSync/upload';
-const FILES_CHANGE_EVENT = 'ide.files.change';
-
-describe('IDE stores/plugins/mirror', () => {
- let store;
-
- beforeEach(() => {
- const root = document.createElement('div');
-
- store = createStore();
- createTerminalPlugin(root)(store);
-
- store.dispatch = jest.fn(() => Promise.resolve());
-
- createTerminalSyncPlugin(root)(store);
- });
-
- it('does nothing on ide.files.change event', () => {
- eventHub.$emit(FILES_CHANGE_EVENT);
-
- expect(store.dispatch).not.toHaveBeenCalled();
- });
-
- describe('when session starts running', () => {
- beforeEach(() => {
- store.commit(`terminal/${SET_SESSION_STATUS}`, RUNNING);
- });
-
- it('starts', () => {
- expect(store.dispatch).toHaveBeenCalledWith(ACTION_START);
- });
-
- it('uploads when ide.files.change is emitted', () => {
- expect(store.dispatch).not.toHaveBeenCalledWith(ACTION_UPLOAD);
-
- eventHub.$emit(FILES_CHANGE_EVENT);
-
- jest.runAllTimers();
-
- expect(store.dispatch).toHaveBeenCalledWith(ACTION_UPLOAD);
- });
-
- it('does nothing when ide.files.change is emitted with "update"', () => {
- eventHub.$emit(FILES_CHANGE_EVENT, createTriggerUpdatePayload('foo'));
-
- jest.runAllTimers();
-
- expect(store.dispatch).not.toHaveBeenCalledWith(ACTION_UPLOAD);
- });
-
- describe('when session stops', () => {
- beforeEach(() => {
- store.commit(`terminal/${SET_SESSION_STATUS}`, STOPPING);
- });
-
- it('stops', () => {
- expect(store.dispatch).toHaveBeenCalledWith(ACTION_STOP);
- });
-
- it('does not upload anymore', () => {
- eventHub.$emit(FILES_CHANGE_EVENT);
-
- jest.runAllTimers();
-
- expect(store.dispatch).not.toHaveBeenCalledWith(ACTION_UPLOAD);
- });
- });
- });
-});
diff --git a/spec/frontend/ide/stores/utils_spec.js b/spec/frontend/ide/stores/utils_spec.js
deleted file mode 100644
index a8875e0cd02..00000000000
--- a/spec/frontend/ide/stores/utils_spec.js
+++ /dev/null
@@ -1,672 +0,0 @@
-import { commitActionTypes } from '~/ide/constants';
-import * as utils from '~/ide/stores/utils';
-import { file } from '../helpers';
-
-describe('Multi-file store utils', () => {
- describe('setPageTitle', () => {
- it('sets the document page title', () => {
- utils.setPageTitle('test');
-
- expect(document.title).toBe('test');
- });
- });
-
- describe('setPageTitleForFile', () => {
- it('sets the document page title for the file passed', () => {
- const f = {
- path: 'README.md',
- };
-
- const state = {
- currentBranchId: 'main',
- currentProjectId: 'test/test',
- };
-
- utils.setPageTitleForFile(state, f);
-
- expect(document.title).toBe('README.md · main · test/test · GitLab');
- });
- });
-
- describe('createCommitPayload', () => {
- it('returns API payload', () => {
- const state = {
- commitMessage: 'commit message',
- };
- const rootState = {
- stagedFiles: [
- {
- ...file('staged'),
- path: 'staged',
- content: 'updated file content',
- lastCommitSha: '123456789',
- },
- {
- ...file('newFile'),
- path: 'added',
- tempFile: true,
- content: 'new file content',
- rawPath: 'blob:https://gitlab.com/048c7ac1-98de-4a37-ab1b-0206d0ea7e1b',
- lastCommitSha: '123456789',
- },
- { ...file('deletedFile'), path: 'deletedFile', deleted: true },
- { ...file('renamedFile'), path: 'renamedFile', prevPath: 'prevPath' },
- ],
- currentBranchId: 'main',
- };
- const payload = utils.createCommitPayload({
- branch: 'main',
- newBranch: false,
- state,
- rootState,
- getters: {},
- });
-
- expect(payload).toEqual({
- branch: 'main',
- commit_message: 'commit message',
- actions: [
- {
- action: commitActionTypes.update,
- file_path: 'staged',
- content: 'updated file content',
- encoding: 'text',
- last_commit_id: '123456789',
- previous_path: undefined,
- },
- {
- action: commitActionTypes.create,
- file_path: 'added',
- // atob("new file content")
- content: 'bmV3IGZpbGUgY29udGVudA==',
- encoding: 'base64',
- last_commit_id: '123456789',
- previous_path: undefined,
- },
- {
- action: commitActionTypes.delete,
- file_path: 'deletedFile',
- content: undefined,
- encoding: 'text',
- last_commit_id: undefined,
- previous_path: undefined,
- },
- {
- action: commitActionTypes.move,
- file_path: 'renamedFile',
- content: undefined,
- encoding: 'text',
- last_commit_id: undefined,
- previous_path: 'prevPath',
- },
- ],
- start_sha: undefined,
- });
- });
-
- it('uses prebuilt commit message when commit message is empty', () => {
- const rootState = {
- stagedFiles: [
- {
- ...file('staged'),
- path: 'staged',
- content: 'updated file content',
- lastCommitSha: '123456789',
- },
- {
- ...file('newFile'),
- path: 'added',
- tempFile: true,
- content: 'new file content',
- rawPath: 'blob:https://gitlab.com/048c7ac1-98de-4a37-ab1b-0206d0ea7e1b',
- lastCommitSha: '123456789',
- },
- ],
- currentBranchId: 'main',
- };
- const payload = utils.createCommitPayload({
- branch: 'main',
- newBranch: false,
- state: {},
- rootState,
- getters: {
- preBuiltCommitMessage: 'prebuilt test commit message',
- },
- });
-
- expect(payload).toEqual({
- branch: 'main',
- commit_message: 'prebuilt test commit message',
- actions: [
- {
- action: commitActionTypes.update,
- file_path: 'staged',
- content: 'updated file content',
- encoding: 'text',
- last_commit_id: '123456789',
- previous_path: undefined,
- },
- {
- action: commitActionTypes.create,
- file_path: 'added',
- // atob("new file content")
- content: 'bmV3IGZpbGUgY29udGVudA==',
- encoding: 'base64',
- last_commit_id: '123456789',
- previous_path: undefined,
- },
- ],
- start_sha: undefined,
- });
- });
- });
-
- describe('commitActionForFile', () => {
- it('returns deleted for deleted file', () => {
- expect(
- utils.commitActionForFile({
- deleted: true,
- }),
- ).toBe(commitActionTypes.delete);
- });
-
- it('returns create for tempFile', () => {
- expect(
- utils.commitActionForFile({
- tempFile: true,
- }),
- ).toBe(commitActionTypes.create);
- });
-
- it('returns move for moved file', () => {
- expect(
- utils.commitActionForFile({
- prevPath: 'test',
- }),
- ).toBe(commitActionTypes.move);
- });
-
- it('returns update by default', () => {
- expect(utils.commitActionForFile({})).toBe(commitActionTypes.update);
- });
- });
-
- describe('getCommitFiles', () => {
- it('filters out folders from the list', () => {
- const files = [
- {
- path: 'a',
- type: 'blob',
- deleted: true,
- },
- {
- path: 'c',
- type: 'tree',
- deleted: true,
- },
- {
- path: 'c/d',
- type: 'blob',
- deleted: true,
- },
- ];
-
- const flattendFiles = utils.getCommitFiles(files);
-
- expect(flattendFiles).toEqual([
- {
- path: 'a',
- type: 'blob',
- deleted: true,
- },
- {
- path: 'c/d',
- type: 'blob',
- deleted: true,
- },
- ]);
- });
- });
-
- describe('mergeTrees', () => {
- let fromTree;
- let toTree;
-
- beforeEach(() => {
- fromTree = [file('foo')];
- toTree = [file('bar')];
- });
-
- it('merges simple trees with sorting the result', () => {
- toTree = [file('beta'), file('alpha'), file('gamma')];
- const res = utils.mergeTrees(fromTree, toTree);
-
- expect(res.length).toEqual(4);
- expect(res[0].name).toEqual('alpha');
- expect(res[1].name).toEqual('beta');
- expect(res[2].name).toEqual('foo');
- expect(res[3].name).toEqual('gamma');
- expect(res[2]).toBe(fromTree[0]);
- });
-
- it('handles edge cases', () => {
- expect(utils.mergeTrees({}, []).length).toEqual(0);
-
- let res = utils.mergeTrees({}, toTree);
-
- expect(res.length).toEqual(1);
- expect(res[0].name).toEqual('bar');
-
- res = utils.mergeTrees(fromTree, []);
-
- expect(res.length).toEqual(1);
- expect(res[0].name).toEqual('foo');
- expect(res[0]).toBe(fromTree[0]);
- });
-
- it('merges simple trees without producing duplicates', () => {
- toTree.push(file('foo'));
-
- const res = utils.mergeTrees(fromTree, toTree);
-
- expect(res.length).toEqual(2);
- expect(res[0].name).toEqual('bar');
- expect(res[1].name).toEqual('foo');
- expect(res[1]).not.toBe(fromTree[0]);
- });
-
- it('merges nested tree into the main one without duplicates', () => {
- fromTree[0].tree.push({
- ...file('alpha'),
- path: 'foo/alpha',
- tree: [{ ...file('beta.md'), path: 'foo/alpha/beta.md' }],
- });
-
- toTree.push({
- ...file('foo'),
- tree: [
- {
- ...file('alpha'),
- path: 'foo/alpha',
- tree: [{ ...file('gamma.md'), path: 'foo/alpha/gamma.md' }],
- },
- ],
- });
-
- const res = utils.mergeTrees(fromTree, toTree);
-
- expect(res.length).toEqual(2);
- expect(res[1].name).toEqual('foo');
-
- const finalBranch = res[1].tree[0].tree;
-
- expect(finalBranch.length).toEqual(2);
- expect(finalBranch[0].name).toEqual('beta.md');
- expect(finalBranch[1].name).toEqual('gamma.md');
- });
-
- it('marks correct folders as opened as the parsing goes on', () => {
- fromTree[0].tree.push({
- ...file('alpha'),
- path: 'foo/alpha',
- tree: [{ ...file('beta.md'), path: 'foo/alpha/beta.md' }],
- });
-
- toTree.push({
- ...file('foo'),
- tree: [
- {
- ...file('alpha'),
- path: 'foo/alpha',
- tree: [{ ...file('gamma.md'), path: 'foo/alpha/gamma.md' }],
- },
- ],
- });
-
- const res = utils.mergeTrees(fromTree, toTree);
-
- expect(res[1].name).toEqual('foo');
- expect(res[1].opened).toEqual(true);
-
- expect(res[1].tree[0].name).toEqual('alpha');
- expect(res[1].tree[0].opened).toEqual(true);
- });
- });
-
- describe('swapInStateArray', () => {
- let localState;
-
- beforeEach(() => {
- localState = [];
- });
-
- it('swaps existing entry with a new one', () => {
- const file1 = { ...file('old'), key: 'foo' };
- const file2 = file('new');
- const arr = [file1];
-
- Object.assign(localState, {
- dummyArray: arr,
- entries: {
- new: file2,
- },
- });
-
- utils.swapInStateArray(localState, 'dummyArray', 'foo', 'new');
-
- expect(localState.dummyArray.length).toBe(1);
- expect(localState.dummyArray[0]).toBe(file2);
- });
-
- it('does not add an item if it does not exist yet in array', () => {
- const file1 = file('file');
- Object.assign(localState, {
- dummyArray: [],
- entries: {
- file: file1,
- },
- });
-
- utils.swapInStateArray(localState, 'dummyArray', 'foo', 'file');
-
- expect(localState.dummyArray.length).toBe(0);
- });
- });
-
- describe('swapInParentTreeWithSorting', () => {
- let localState;
- let branchInfo;
- const currentProjectId = '123-foo';
- const currentBranchId = 'main';
-
- beforeEach(() => {
- localState = {
- currentBranchId,
- currentProjectId,
- trees: {
- [`${currentProjectId}/${currentBranchId}`]: {
- tree: [],
- },
- },
- entries: {
- oldPath: file('oldPath', 'oldPath', 'blob'),
- newPath: file('newPath', 'newPath', 'blob'),
- parentPath: file('parentPath', 'parentPath', 'tree'),
- },
- };
- branchInfo = localState.trees[`${currentProjectId}/${currentBranchId}`];
- });
-
- it('does not change tree if newPath is not supplied', () => {
- branchInfo.tree = [localState.entries.oldPath];
-
- utils.swapInParentTreeWithSorting(localState, 'oldPath', undefined, undefined);
-
- expect(branchInfo.tree).toEqual([localState.entries.oldPath]);
- });
-
- describe('oldPath to replace is not defined: simple addition to tree', () => {
- it('adds to tree on the state if there is no parent for the entry', () => {
- expect(branchInfo.tree.length).toBe(0);
-
- utils.swapInParentTreeWithSorting(localState, undefined, 'oldPath', undefined);
-
- expect(branchInfo.tree.length).toBe(1);
- expect(branchInfo.tree[0].name).toBe('oldPath');
-
- utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', undefined);
-
- expect(branchInfo.tree.length).toBe(2);
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'newPath',
- }),
- expect.objectContaining({
- name: 'oldPath',
- }),
- ]);
- });
-
- it('adds to parent tree if it is supplied', () => {
- utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', 'parentPath');
-
- expect(localState.entries.parentPath.tree.length).toBe(1);
- expect(localState.entries.parentPath.tree).toEqual([
- expect.objectContaining({
- name: 'newPath',
- }),
- ]);
-
- localState.entries.parentPath.tree = [localState.entries.oldPath];
-
- utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', 'parentPath');
-
- expect(localState.entries.parentPath.tree.length).toBe(2);
- expect(localState.entries.parentPath.tree).toEqual([
- expect.objectContaining({
- name: 'newPath',
- }),
- expect.objectContaining({
- name: 'oldPath',
- }),
- ]);
- });
- });
-
- describe('swapping of the items', () => {
- it('swaps entries if both paths are supplied', () => {
- branchInfo.tree = [localState.entries.oldPath];
-
- utils.swapInParentTreeWithSorting(localState, localState.entries.oldPath.key, 'newPath');
-
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'newPath',
- }),
- ]);
-
- utils.swapInParentTreeWithSorting(localState, localState.entries.newPath.key, 'oldPath');
-
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'oldPath',
- }),
- ]);
- });
-
- it('sorts tree after swapping the entries', () => {
- const alpha = file('alpha', 'alpha', 'blob');
- const beta = file('beta', 'beta', 'blob');
- const gamma = file('gamma', 'gamma', 'blob');
- const theta = file('theta', 'theta', 'blob');
- localState.entries = {
- alpha,
- beta,
- gamma,
- theta,
- };
-
- branchInfo.tree = [alpha, beta, gamma];
-
- utils.swapInParentTreeWithSorting(localState, alpha.key, 'theta');
-
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'beta',
- }),
- expect.objectContaining({
- name: 'gamma',
- }),
- expect.objectContaining({
- name: 'theta',
- }),
- ]);
-
- utils.swapInParentTreeWithSorting(localState, gamma.key, 'alpha');
-
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'alpha',
- }),
- expect.objectContaining({
- name: 'beta',
- }),
- expect.objectContaining({
- name: 'theta',
- }),
- ]);
-
- utils.swapInParentTreeWithSorting(localState, beta.key, 'gamma');
-
- expect(branchInfo.tree).toEqual([
- expect.objectContaining({
- name: 'alpha',
- }),
- expect.objectContaining({
- name: 'gamma',
- }),
- expect.objectContaining({
- name: 'theta',
- }),
- ]);
- });
- });
- });
-
- describe('cleanTrailingSlash', () => {
- [
- {
- input: '',
- output: '',
- },
- {
- input: 'abc',
- output: 'abc',
- },
- {
- input: 'abc/',
- output: 'abc',
- },
- {
- input: 'abc/def',
- output: 'abc/def',
- },
- {
- input: 'abc/def/',
- output: 'abc/def',
- },
- ].forEach(({ input, output }) => {
- it(`cleans trailing slash from string "${input}"`, () => {
- expect(utils.cleanTrailingSlash(input)).toEqual(output);
- });
- });
- });
-
- describe('pathsAreEqual', () => {
- [
- {
- args: ['abc', 'abc'],
- output: true,
- },
- {
- args: ['abc', 'def'],
- output: false,
- },
- {
- args: ['abc/', 'abc'],
- output: true,
- },
- {
- args: ['abc/abc', 'abc'],
- output: false,
- },
- {
- args: ['/', ''],
- output: true,
- },
- {
- args: ['', '/'],
- output: true,
- },
- {
- args: [false, '/'],
- output: true,
- },
- ].forEach(({ args, output }) => {
- it(`cleans and tests equality (${JSON.stringify(args)})`, () => {
- expect(utils.pathsAreEqual(...args)).toEqual(output);
- });
- });
- });
-
- describe('extractMarkdownImagesFromEntries', () => {
- let mdFile;
- let entries;
-
- beforeEach(() => {
- const img = { content: 'png-gibberish', rawPath: 'blob:1234' };
- mdFile = { path: 'path/to/some/directory/myfile.md' };
- entries = {
- // invalid (or lack of) extensions are also supported as long as there's
- // a real image inside and can go into an