summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue65
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue35
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue36
-rw-r--r--app/assets/javascripts/ide/components/ide.vue99
-rw-r--r--app/assets/javascripts/ide/components/ide_context_bar.vue108
-rw-r--r--app/assets/javascripts/ide/components/ide_project_branches_tree.vue47
-rw-r--r--app/assets/javascripts/ide/components/ide_project_tree.vue49
-rw-r--r--app/assets/javascripts/ide/components/ide_repo_tree.vue74
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue114
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue66
-rw-r--r--app/assets/javascripts/ide/components/new_branch_form.vue108
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue101
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue112
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue87
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue171
-rw-r--r--app/assets/javascripts/ide/components/repo_edit_button.vue57
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue136
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue165
-rw-r--r--app/assets/javascripts/ide/components/repo_file_buttons.vue60
-rw-r--r--app/assets/javascripts/ide/components/repo_loading_file.vue42
-rw-r--r--app/assets/javascripts/ide/components/repo_prev_directory.vue32
-rw-r--r--app/assets/javascripts/ide/components/repo_preview.vue71
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue74
-rw-r--r--app/assets/javascripts/ide/components/repo_tabs.vue27
-rw-r--r--app/assets/javascripts/ide/ide_router.js101
-rw-r--r--app/assets/javascripts/ide/index.js31
-rw-r--r--app/assets/javascripts/ide/lib/common/disposable.js14
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js64
-rw-r--r--app/assets/javascripts/ide/lib/common/model_manager.js32
-rw-r--r--app/assets/javascripts/ide/lib/decorations/controller.js43
-rw-r--r--app/assets/javascripts/ide/lib/diff/controller.js71
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff.js30
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff_worker.js10
-rw-r--r--app/assets/javascripts/ide/lib/editor.js110
-rw-r--r--app/assets/javascripts/ide/lib/editor_options.js2
-rw-r--r--app/assets/javascripts/ide/monaco_loader.js16
-rw-r--r--app/assets/javascripts/ide/services/index.js47
-rw-r--r--app/assets/javascripts/ide/stores/actions.js196
-rw-r--r--app/assets/javascripts/ide/stores/actions/branch.js43
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js137
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js27
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js188
-rw-r--r--app/assets/javascripts/ide/stores/getters.js19
-rw-r--r--app/assets/javascripts/ide/stores/index.js15
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js46
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js70
-rw-r--r--app/assets/javascripts/ide/stores/mutations/branch.js28
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js74
-rw-r--r--app/assets/javascripts/ide/stores/mutations/project.js23
-rw-r--r--app/assets/javascripts/ide/stores/mutations/tree.js36
-rw-r--r--app/assets/javascripts/ide/stores/state.js23
-rw-r--r--app/assets/javascripts/ide/stores/utils.js177
-rw-r--r--app/controllers/ide_controller.rb6
-rw-r--r--app/finders/todos_finder.rb15
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/blob_helper.rb14
-rw-r--r--app/helpers/notes_helper.rb2
-rw-r--r--app/models/lfs_object.rb4
-rw-r--r--app/services/notes/quick_actions_service.rb8
-rw-r--r--app/views/ide/index.html.haml11
-rw-r--r--app/views/projects/blob/_header.html.haml1
-rw-r--r--app/views/projects/tree/_tree_header.html.haml5
-rw-r--r--app/views/shared/_ref_switcher.html.haml12
-rw-r--r--changelogs/unreleased/43334-reply-by-email-did-not-pick-up-unsubscribe-quick-action.yml5
-rw-r--r--changelogs/unreleased/ee-4862-verify-file-checksums.yml5
-rw-r--r--changelogs/unreleased/remove-projects-finder-from-todos-finder.yml5
-rw-r--r--config/routes.rb2
-rw-r--r--config/webpack.config.js1
-rw-r--r--doc/administration/raketasks/check.md27
-rw-r--r--doc/development/database_debugging.md35
-rw-r--r--doc/development/i18n/proofreader.md2
-rw-r--r--doc/development/rake_tasks.md6
-rw-r--r--doc/ssh/README.md51
-rw-r--r--lib/gitlab/ci/pipeline/chain/create.rb16
-rw-r--r--lib/gitlab/verify/batch_verifier.rb64
-rw-r--r--lib/gitlab/verify/lfs_objects.rb27
-rw-r--r--lib/gitlab/verify/rake_task.rb53
-rw-r--r--lib/gitlab/verify/uploads.rb27
-rw-r--r--lib/tasks/gitlab/lfs/check.rake8
-rw-r--r--lib/tasks/gitlab/uploads.rake44
-rw-r--r--lib/tasks/gitlab/uploads/check.rake8
-rw-r--r--spec/factories/lfs_objects.rb6
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb3
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb57
-rw-r--r--spec/features/projects/tree/create_file_spec.rb47
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb53
-rw-r--r--spec/fixtures/emails/update_commands_only_reply.eml38
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js33
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_item_spec.js53
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_spec.js59
-rw-r--r--spec/javascripts/repo/components/ide_context_bar_spec.js49
-rw-r--r--spec/javascripts/repo/components/ide_repo_tree_spec.js63
-rw-r--r--spec/javascripts/repo/components/ide_side_bar_spec.js43
-rw-r--r--spec/javascripts/repo/components/ide_spec.js39
-rw-r--r--spec/javascripts/repo/components/new_branch_form_spec.js114
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js77
-rw-r--r--spec/javascripts/repo/components/new_dropdown/modal_spec.js237
-rw-r--r--spec/javascripts/repo/components/new_dropdown/upload_spec.js158
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js140
-rw-r--r--spec/javascripts/repo/components/repo_edit_button_spec.js83
-rw-r--r--spec/javascripts/repo/components/repo_editor_spec.js60
-rw-r--r--spec/javascripts/repo/components/repo_file_buttons_spec.js49
-rw-r--r--spec/javascripts/repo/components/repo_file_spec.js98
-rw-r--r--spec/javascripts/repo/components/repo_loading_file_spec.js63
-rw-r--r--spec/javascripts/repo/components/repo_prev_directory_spec.js45
-rw-r--r--spec/javascripts/repo/components/repo_preview_spec.js37
-rw-r--r--spec/javascripts/repo/components/repo_tab_spec.js108
-rw-r--r--spec/javascripts/repo/components/repo_tabs_spec.js37
-rw-r--r--spec/javascripts/repo/helpers.js16
-rw-r--r--spec/javascripts/repo/lib/common/disposable_spec.js44
-rw-r--r--spec/javascripts/repo/lib/common/model_manager_spec.js81
-rw-r--r--spec/javascripts/repo/lib/common/model_spec.js84
-rw-r--r--spec/javascripts/repo/lib/decorations/controller_spec.js120
-rw-r--r--spec/javascripts/repo/lib/diff/controller_spec.js176
-rw-r--r--spec/javascripts/repo/lib/diff/diff_spec.js80
-rw-r--r--spec/javascripts/repo/lib/editor_options_spec.js7
-rw-r--r--spec/javascripts/repo/lib/editor_spec.js128
-rw-r--r--spec/javascripts/repo/monaco_loader_spec.js13
-rw-r--r--spec/javascripts/repo/stores/actions/branch_spec.js44
-rw-r--r--spec/javascripts/repo/stores/actions/file_spec.js431
-rw-r--r--spec/javascripts/repo/stores/actions/tree_spec.js350
-rw-r--r--spec/javascripts/repo/stores/actions_spec.js432
-rw-r--r--spec/javascripts/repo/stores/getters_spec.js114
-rw-r--r--spec/javascripts/repo/stores/mutations/branch_spec.js18
-rw-r--r--spec/javascripts/repo/stores/mutations/file_spec.js131
-rw-r--r--spec/javascripts/repo/stores/mutations/tree_spec.js71
-rw-r--r--spec/javascripts/repo/stores/mutations_spec.js125
-rw-r--r--spec/javascripts/repo/stores/utils_spec.js119
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb27
-rw-r--r--spec/lib/gitlab/verify/lfs_objects_spec.rb35
-rw-r--r--spec/lib/gitlab/verify/uploads_spec.rb44
-rw-r--r--spec/services/notes/create_service_spec.rb51
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb30
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb5
-rw-r--r--spec/support/gitlab_verify.rb45
-rw-r--r--spec/tasks/gitlab/lfs/check_rake_spec.rb28
-rw-r--r--spec/tasks/gitlab/uploads/check_rake_spec.rb (renamed from spec/tasks/gitlab/uploads_rake_spec.rb)13
138 files changed, 585 insertions, 8206 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8a0c9802c15..8b489f1a07c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -619,9 +619,10 @@ codequality:
cache: {}
dependencies: []
script:
+ - apk update && apk add jq
- ./scripts/codequality analyze -f json > raw_codeclimate.json || true
# The following line keeps only the fields used in the MR widget, reducing the JSON artifact size
- - cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,description,fingerprint,location})' > codeclimate.json
+ - jq -c 'map({check_name,description,fingerprint,location})' raw_codeclimate.json > codeclimate.json
artifacts:
paths: [codeclimate.json]
expire_in: 1 week
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
deleted file mode 100644
index a8459b011df..00000000000
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import icon from '../../../vue_shared/components/icon.vue';
- import listItem from './list_item.vue';
- import listCollapsed from './list_collapsed.vue';
-
- export default {
- components: {
- icon,
- listItem,
- listCollapsed,
- },
- props: {
- title: {
- type: String,
- required: true,
- },
- fileList: {
- type: Array,
- required: true,
- },
- },
- computed: {
- ...mapState([
- 'currentProjectId',
- 'currentBranchId',
- 'rightPanelCollapsed',
- ]),
- },
- methods: {
- toggleCollapsed() {
- this.$emit('toggleCollapsed');
- },
- },
- };
-</script>
-
-<template>
- <div class="multi-file-commit-list">
- <list-collapsed
- v-if="rightPanelCollapsed"
- />
- <template v-else>
- <ul
- v-if="fileList.length"
- class="list-unstyled append-bottom-0"
- >
- <li
- v-for="file in fileList"
- :key="file.key"
- >
- <list-item
- :file="file"
- />
- </li>
- </ul>
- <div
- v-else
- class="help-block prepend-top-0"
- >
- No changes
- </div>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
deleted file mode 100644
index 6a0262f271b..00000000000
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
- import { mapGetters } from 'vuex';
- import icon from '../../../vue_shared/components/icon.vue';
-
- export default {
- components: {
- icon,
- },
- computed: {
- ...mapGetters([
- 'addedFiles',
- 'modifiedFiles',
- ]),
- },
- };
-</script>
-
-<template>
- <div
- class="multi-file-commit-list-collapsed text-center"
- >
- <icon
- name="file-addition"
- :size="18"
- css-classes="multi-file-addition append-bottom-10"
- />
- {{ addedFiles.length }}
- <icon
- name="file-modified"
- :size="18"
- css-classes="multi-file-modified prepend-top-10 append-bottom-10"
- />
- {{ modifiedFiles.length }}
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
deleted file mode 100644
index 742f746e02f..00000000000
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<script>
- import icon from '../../../vue_shared/components/icon.vue';
-
- export default {
- components: {
- icon,
- },
- props: {
- file: {
- type: Object,
- required: true,
- },
- },
- computed: {
- iconName() {
- return this.file.tempFile ? 'file-addition' : 'file-modified';
- },
- iconClass() {
- return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
- },
- },
- };
-</script>
-
-<template>
- <div class="multi-file-commit-list-item">
- <icon
- :name="iconName"
- :size="16"
- :css-classes="iconClass"
- />
- <span class="multi-file-commit-list-path">
- {{ file.path }}
- </span>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
deleted file mode 100644
index 89981ab2c65..00000000000
--- a/app/assets/javascripts/ide/components/ide.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<script>
- import { mapState, mapGetters } from 'vuex';
- import ideSidebar from './ide_side_bar.vue';
- import ideContextbar from './ide_context_bar.vue';
- import repoTabs from './repo_tabs.vue';
- import repoFileButtons from './repo_file_buttons.vue';
- import ideStatusBar from './ide_status_bar.vue';
- import repoPreview from './repo_preview.vue';
- import repoEditor from './repo_editor.vue';
-
- export default {
- components: {
- ideSidebar,
- ideContextbar,
- repoTabs,
- repoFileButtons,
- ideStatusBar,
- repoEditor,
- repoPreview,
- },
- props: {
- emptyStateSvgPath: {
- type: String,
- required: true,
- },
- },
- computed: {
- ...mapState([
- 'currentBlobView',
- 'selectedFile',
- ]),
- ...mapGetters([
- 'changedFiles',
- 'activeFile',
- ]),
- },
- mounted() {
- const returnValue = 'Are you sure you want to lose unsaved changes?';
- window.onbeforeunload = (e) => {
- if (!this.changedFiles.length) return undefined;
-
- Object.assign(e, {
- returnValue,
- });
- return returnValue;
- };
- },
- };
-</script>
-
-<template>
- <div
- class="ide-view"
- >
- <ide-sidebar />
- <div
- class="multi-file-edit-pane"
- >
- <template
- v-if="activeFile"
- >
- <repo-tabs/>
- <component
- class="multi-file-edit-pane-content"
- :is="currentBlobView"
- />
- <repo-file-buttons />
- <ide-status-bar
- :file="selectedFile"
- />
- </template>
- <template
- v-else
- >
- <div class="ide-empty-state">
- <div class="row js-empty-state">
- <div class="col-xs-12">
- <div class="svg-content svg-250">
- <img :src="emptyStateSvgPath" />
- </div>
- </div>
- <div class="col-xs-12">
- <div class="text-content text-center">
- <h4>
- Welcome to the GitLab IDE
- </h4>
- <p>
- You can select a file in the left sidebar to begin
- editing and use the right sidebar to commit your changes.
- </p>
- </div>
- </div>
- </div>
- </div>
- </template>
- </div>
- <ide-contextbar/>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue
deleted file mode 100644
index 9d933b8891d..00000000000
--- a/app/assets/javascripts/ide/components/ide_context_bar.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-<script>
- import { mapGetters, mapState, mapActions } from 'vuex';
- import icon from '~/vue_shared/components/icon.vue';
- import panelResizer from '~/vue_shared/components/panel_resizer.vue';
- import repoCommitSection from './repo_commit_section.vue';
-
- export default {
- components: {
- repoCommitSection,
- icon,
- panelResizer,
- },
- data() {
- return {
- width: 290,
- };
- },
- computed: {
- ...mapState([
- 'rightPanelCollapsed',
- ]),
- ...mapGetters([
- 'changedFiles',
- ]),
- currentIcon() {
- return this.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
- },
- maxSize() {
- return window.innerWidth / 2;
- },
- panelStyle() {
- if (!this.rightPanelCollapsed) {
- return { width: `${this.width}px` };
- }
- return {};
- },
- },
- methods: {
- ...mapActions([
- 'setPanelCollapsedStatus',
- 'setResizingStatus',
- ]),
- toggleCollapsed() {
- this.setPanelCollapsedStatus({
- side: 'right',
- collapsed: !this.rightPanelCollapsed,
- });
- },
- resizingStarted() {
- this.setResizingStatus(true);
- },
- resizingEnded() {
- this.setResizingStatus(false);
- },
- },
- };
-</script>
-
-<template>
- <div
- class="multi-file-commit-panel"
- :class="{
- 'is-collapsed': rightPanelCollapsed,
- }"
- :style="panelStyle"
- >
- <div class="multi-file-commit-panel-section">
- <header
- class="multi-file-commit-panel-header"
- :class="{
- 'is-collapsed': rightPanelCollapsed,
- }"
- >
- <div
- class="multi-file-commit-panel-header-title"
- v-if="!rightPanelCollapsed"
- >
- <icon
- name="list-bulleted"
- :size="18"
- />
- Staged
- </div>
- <button
- type="button"
- class="btn btn-transparent multi-file-commit-panel-collapse-btn"
- @click="toggleCollapsed"
- >
- <icon
- :name="currentIcon"
- :size="18"
- />
- </button>
- </header>
- <repo-commit-section />
- </div>
- <panel-resizer
- :size.sync="width"
- :enabled="!rightPanelCollapsed"
- :start-size="290"
- :min-size="200"
- :max-size="maxSize"
- @resize-start="resizingStarted"
- @resize-end="resizingEnded"
- side="left"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
deleted file mode 100644
index 2fbff2bd789..00000000000
--- a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
+++ /dev/null
@@ -1,47 +0,0 @@
-<script>
-import icon from '~/vue_shared/components/icon.vue';
-import repoTree from './ide_repo_tree.vue';
-import newDropdown from './new_dropdown/index.vue';
-
-export default {
- components: {
- repoTree,
- icon,
- newDropdown,
- },
- props: {
- projectId: {
- type: String,
- required: true,
- },
- branch: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="branch-container">
- <div class="branch-header">
- <div class="branch-header-title">
- <icon
- name="branch"
- :size="12"
- />
- {{ branch.name }}
- </div>
- <div class="branch-header-btns">
- <new-dropdown
- :project-id="projectId"
- :branch="branch.name"
- path=""
- />
- </div>
- </div>
- <div>
- <repo-tree :tree-id="branch.treeId" />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue
deleted file mode 100644
index 32bf7175c88..00000000000
--- a/app/assets/javascripts/ide/components/ide_project_tree.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<script>
-import projectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
-import branchesTree from './ide_project_branches_tree.vue';
-
-export default {
- components: {
- branchesTree,
- projectAvatarImage,
- },
- props: {
- project: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="projects-sidebar">
- <div class="context-header">
- <a
- :title="project.name"
- :href="project.web_url"
- >
- <div class="avatar-container s40 project-avatar">
- <project-avatar-image
- class="avatar-container project-avatar"
- :link-href="project.path"
- :img-src="project.avatar_url"
- :img-alt="project.name"
- :img-size="40"
- />
- </div>
- <div class="sidebar-context-title">
- {{ project.name }}
- </div>
- </a>
- </div>
- <div class="multi-file-commit-panel-inner-scroll">
- <branches-tree
- v-for="branch in project.branches"
- :key="branch.name"
- :project-id="project.path_with_namespace"
- :branch="branch"
- />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue
deleted file mode 100644
index 4a324264992..00000000000
--- a/app/assets/javascripts/ide/components/ide_repo_tree.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<script>
-import { mapState } from 'vuex';
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import repoPreviousDirectory from './repo_prev_directory.vue';
-import repoFile from './repo_file.vue';
-import { treeList } from '../stores/utils';
-
-export default {
- components: {
- repoPreviousDirectory,
- repoFile,
- skeletonLoadingContainer,
- },
- props: {
- treeId: {
- type: String,
- required: true,
- },
- },
- computed: {
- ...mapState([
- 'trees',
- 'isRoot',
- ]),
- ...mapState({
- projectName(state) {
- return state.project.name;
- },
- }),
- fetchedList() {
- return treeList(this.$store.state, this.treeId);
- },
- hasPreviousDirectory() {
- return !this.isRoot && this.fetchedList.length;
- },
- showLoading() {
- if (this.trees[this.treeId]) {
- return this.trees[this.treeId].loading;
- }
- return true;
- },
- },
-};
-</script>
-
-<template>
- <div>
- <div class="ide-file-list">
- <table class="table">
- <tbody
- v-if="treeId"
- >
- <repo-previous-directory
- v-if="hasPreviousDirectory"
- />
- <template v-if="showLoading">
- <div
- class="multi-file-loading-container"
- v-for="n in 3"
- :key="n"
- >
- <skeleton-loading-container />
- </div>
- </template>
- <repo-file
- v-for="file in fetchedList"
- :key="file.key"
- :file="file"
- />
- </tbody>
- </table>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
deleted file mode 100644
index 18b5059a17f..00000000000
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<script>
- import { mapState, mapActions } from 'vuex';
- import icon from '~/vue_shared/components/icon.vue';
- import panelResizer from '~/vue_shared/components/panel_resizer.vue';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
- import projectTree from './ide_project_tree.vue';
-
- export default {
- components: {
- projectTree,
- icon,
- panelResizer,
- skeletonLoadingContainer,
- },
- data() {
- return {
- width: 290,
- };
- },
- computed: {
- ...mapState([
- 'loading',
- 'projects',
- 'leftPanelCollapsed',
- ]),
- currentIcon() {
- return this.leftPanelCollapsed ? 'angle-double-right' : 'angle-double-left';
- },
- maxSize() {
- return window.innerWidth / 2;
- },
- panelStyle() {
- if (!this.leftPanelCollapsed) {
- return { width: `${this.width}px` };
- }
- return {};
- },
- showLoading() {
- return this.loading;
- },
- },
- methods: {
- ...mapActions([
- 'setPanelCollapsedStatus',
- 'setResizingStatus',
- ]),
- toggleCollapsed() {
- this.setPanelCollapsedStatus({
- side: 'left',
- collapsed: !this.leftPanelCollapsed,
- });
- },
- resizingStarted() {
- this.setResizingStatus(true);
- },
- resizingEnded() {
- this.setResizingStatus(false);
- },
- },
- };
-</script>
-
-<template>
- <div
- class="multi-file-commit-panel"
- :class="{
- 'is-collapsed': leftPanelCollapsed,
- }"
- :style="panelStyle"
- >
- <div class="multi-file-commit-panel-inner">
- <template v-if="showLoading">
- <div
- class="multi-file-loading-container"
- v-for="n in 3"
- :key="n"
- >
- <skeleton-loading-container />
- </div>
- </template>
- <project-tree
- v-for="project in projects"
- :key="project.id"
- :project="project"
- />
- </div>
- <button
- type="button"
- class="btn btn-transparent left-collapse-btn"
- @click="toggleCollapsed"
- >
- <icon
- :name="currentIcon"
- :size="18"
- />
- <span
- v-if="!leftPanelCollapsed"
- class="collapse-text"
- >
- Collapse sidebar
- </span>
- </button>
- <panel-resizer
- :size.sync="width"
- :enabled="!leftPanelCollapsed"
- :start-size="290"
- :min-size="200"
- :max-size="maxSize"
- @resize-start="resizingStarted"
- @resize-end="resizingEnded"
- side="right"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
deleted file mode 100644
index 97ae64b206d..00000000000
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import icon from '~/vue_shared/components/icon.vue';
- import tooltip from '~/vue_shared/directives/tooltip';
- import timeAgoMixin from '~/vue_shared/mixins/timeago';
-
- export default {
- components: {
- icon,
- },
- directives: {
- tooltip,
- },
- mixins: [
- timeAgoMixin,
- ],
- props: {
- file: {
- type: Object,
- required: true,
- },
- },
- computed: {
- ...mapState([
- 'selectedFile',
- ]),
- },
- };
-</script>
-
-<template>
- <div class="ide-status-bar">
- <div>
- <icon
- name="branch"
- :size="12"
- />
- {{ selectedFile.branchId }}
- </div>
- <div>
- <div v-if="selectedFile.lastCommit && selectedFile.lastCommit.id">
- Last commit:
- <a
- v-tooltip
- :title="selectedFile.lastCommit.message"
- :href="selectedFile.lastCommit.url"
- >
- {{ timeFormated(selectedFile.lastCommit.updatedAt) }} by
- {{ selectedFile.lastCommit.author }}
- </a>
- </div>
- </div>
- <div class="text-right">
- {{ selectedFile.name }}
- </div>
- <div class="text-right">
- {{ selectedFile.eol }}
- </div>
- <div class="text-right">
- {{ file.editorRow }}:{{ file.editorColumn }}
- </div>
- <div class="text-right">
- {{ selectedFile.fileLanguage }}
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/new_branch_form.vue b/app/assets/javascripts/ide/components/new_branch_form.vue
deleted file mode 100644
index 1e8d5bb6453..00000000000
--- a/app/assets/javascripts/ide/components/new_branch_form.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-<script>
- import { mapState, mapActions } from 'vuex';
- import flash, { hideFlash } from '~/flash';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
-
- export default {
- components: {
- loadingIcon,
- },
- data() {
- return {
- branchName: '',
- loading: false,
- };
- },
- computed: {
- ...mapState([
- 'currentBranch',
- ]),
- btnDisabled() {
- return this.loading || this.branchName === '';
- },
- },
- created() {
- // Dropdown is outside of Vue instance & is controlled by Bootstrap
- this.$dropdown = $('.git-revision-dropdown');
-
- // text element is outside Vue app
- this.dropdownText = document.querySelector('.project-refs-form .dropdown-toggle-text');
- },
- methods: {
- ...mapActions([
- 'createNewBranch',
- ]),
- toggleDropdown() {
- this.$dropdown.dropdown('toggle');
- },
- submitNewBranch() {
- // need to query as the element is appended outside of Vue
- const flashEl = this.$refs.flashContainer.querySelector('.flash-alert');
-
- this.loading = true;
-
- if (flashEl) {
- hideFlash(flashEl, false);
- }
-
- this.createNewBranch(this.branchName)
- .then(() => {
- this.loading = false;
- this.branchName = '';
-
- if (this.dropdownText) {
- this.dropdownText.textContent = this.currentBranchId;
- }
-
- this.toggleDropdown();
- })
- .catch(res => res.json().then((data) => {
- this.loading = false;
- flash(data.message, 'alert', this.$el);
- }));
- },
- },
- };
-</script>
-
-<template>
- <div>
- <div
- class="flash-container"
- ref="flashContainer"
- >
- </div>
- <p>
- Create from:
- <code>{{ currentBranch }}</code>
- </p>
- <input
- class="form-control js-new-branch-name"
- type="text"
- placeholder="Name new branch"
- v-model="branchName"
- @keyup.enter.stop.prevent="submitNewBranch"
- />
- <div class="prepend-top-default clearfix">
- <button
- type="button"
- class="btn btn-primary pull-left"
- :disabled="btnDisabled"
- @click.stop.prevent="submitNewBranch"
- >
- <loading-icon
- v-if="loading"
- :inline="true"
- />
- <span>Create</span>
- </button>
- <button
- type="button"
- class="btn btn-default pull-right"
- @click.stop.prevent="toggleDropdown"
- >
- Cancel
- </button>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
deleted file mode 100644
index ef653357f5f..00000000000
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<script>
- import newModal from './modal.vue';
- import upload from './upload.vue';
- import icon from '../../../vue_shared/components/icon.vue';
-
- export default {
- components: {
- icon,
- newModal,
- upload,
- },
- props: {
- branch: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: true,
- },
- parent: {
- type: Object,
- default: null,
- },
- },
- data() {
- return {
- openModal: false,
- modalType: '',
- };
- },
- methods: {
- createNewItem(type) {
- this.modalType = type;
- this.openModal = true;
- },
- hideModal() {
- this.openModal = false;
- },
- },
- };
-</script>
-
-<template>
- <div class="repo-new-btn pull-right">
- <div class="dropdown">
- <button
- type="button"
- class="btn btn-sm btn-default dropdown-toggle add-to-tree"
- data-toggle="dropdown"
- aria-label="Create new file or directory"
- >
- <icon
- name="plus"
- :size="12"
- css-classes="pull-left"
- />
- <icon
- name="arrow-down"
- :size="12"
- css-classes="pull-left"
- />
- </button>
- <ul class="dropdown-menu dropdown-menu-right">
- <li>
- <a
- href="#"
- role="button"
- @click.prevent="createNewItem('blob')"
- >
- {{ __('New file') }}
- </a>
- </li>
- <li>
- <upload
- :branch-id="branch"
- :path="path"
- :parent="parent"
- />
- </li>
- <li>
- <a
- href="#"
- role="button"
- @click.prevent="createNewItem('tree')"
- >
- {{ __('New directory') }}
- </a>
- </li>
- </ul>
- </div>
- <new-modal
- v-if="openModal"
- :type="modalType"
- :branch-id="branch"
- :path="path"
- :parent="parent"
- @hide="hideModal"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
deleted file mode 100644
index 36cd825c6dd..00000000000
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<script>
- import { mapActions, mapState } from 'vuex';
- import { __ } from '../../../locale';
- import modal from '../../../vue_shared/components/modal.vue';
-
- export default {
- components: {
- modal,
- },
- props: {
- branchId: {
- type: String,
- required: true,
- },
- parent: {
- type: Object,
- default: null,
- },
- type: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- entryName: this.path !== '' ? `${this.path}/` : '',
- };
- },
- computed: {
- ...mapState([
- 'currentProjectId',
- ]),
- modalTitle() {
- if (this.type === 'tree') {
- return __('Create new directory');
- }
-
- return __('Create new file');
- },
- buttonLabel() {
- if (this.type === 'tree') {
- return __('Create directory');
- }
-
- return __('Create file');
- },
- formLabelName() {
- if (this.type === 'tree') {
- return __('Directory name');
- }
-
- return __('File name');
- },
- },
- mounted() {
- this.$refs.fieldName.focus();
- },
- methods: {
- ...mapActions([
- 'createTempEntry',
- ]),
- createEntryInStore() {
- this.createTempEntry({
- projectId: this.currentProjectId,
- branchId: this.branchId,
- parent: this.parent,
- name: this.entryName.replace(new RegExp(`^${this.path}/`), ''),
- type: this.type,
- });
-
- this.hideModal();
- },
- hideModal() {
- this.$emit('hide');
- },
- },
- };
-</script>
-
-<template>
- <modal
- :title="modalTitle"
- :primary-button-label="buttonLabel"
- kind="success"
- @cancel="hideModal"
- @submit="createEntryInStore"
- >
- <form
- class="form-horizontal"
- slot="body"
- @submit.prevent="createEntryInStore"
- >
- <fieldset class="form-group append-bottom-0">
- <label class="label-light col-sm-3">
- {{ formLabelName }}
- </label>
- <div class="col-sm-9">
- <input
- type="text"
- class="form-control"
- v-model="entryName"
- ref="fieldName"
- />
- </div>
- </fieldset>
- </form>
- </modal>
-</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
deleted file mode 100644
index 6244737fa43..00000000000
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ /dev/null
@@ -1,87 +0,0 @@
-<script>
- import { mapActions, mapState } from 'vuex';
-
- export default {
- props: {
- branchId: {
- type: String,
- required: true,
- },
- parent: {
- type: Object,
- default: null,
- },
- },
- computed: {
- ...mapState([
- 'trees',
- 'currentProjectId',
- ]),
- },
- mounted() {
- this.$refs.fileUpload.addEventListener('change', this.openFile);
- },
- beforeDestroy() {
- this.$refs.fileUpload.removeEventListener('change', this.openFile);
- },
- methods: {
- ...mapActions([
- 'createTempEntry',
- ]),
- createFile(target, file, isText) {
- const { name } = file;
- let { result } = target;
-
- if (!isText) {
- result = result.split('base64,')[1];
- }
-
- this.createTempEntry({
- name,
- projectId: this.currentProjectId,
- branchId: this.branchId,
- parent: this.parent,
- type: 'blob',
- content: result,
- base64: !isText,
- });
- },
- readFile(file) {
- const reader = new FileReader();
- const isText = file.type.match(/text.*/) !== null;
-
- reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
-
- if (isText) {
- reader.readAsText(file);
- } else {
- reader.readAsDataURL(file);
- }
- },
- openFile() {
- Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
- },
- startFileUpload() {
- this.$refs.fileUpload.click();
- },
- },
- };
-</script>
-
-<template>
- <div>
- <a
- href="#"
- role="button"
- @click.prevent="startFileUpload"
- >
- {{ __('Upload file') }}
- </a>
- <input
- id="file-upload"
- type="file"
- class="hidden"
- ref="fileUpload"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
deleted file mode 100644
index 37f2cf30a29..00000000000
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<script>
-import { mapGetters, mapState, mapActions } from 'vuex';
-import tooltip from '~/vue_shared/directives/tooltip';
-import icon from '~/vue_shared/components/icon.vue';
-import modal from '~/vue_shared/components/modal.vue';
-import commitFilesList from './commit_sidebar/list.vue';
-
-export default {
- components: {
- modal,
- icon,
- commitFilesList,
- },
- directives: {
- tooltip,
- },
- data() {
- return {
- showNewBranchModal: false,
- submitCommitsLoading: false,
- startNewMR: false,
- commitMessage: '',
- };
- },
- computed: {
- ...mapState([
- 'currentProjectId',
- 'currentBranchId',
- 'rightPanelCollapsed',
- ]),
- ...mapGetters([
- 'changedFiles',
- ]),
- commitButtonDisabled() {
- return this.commitMessage === '' || this.submitCommitsLoading || !this.changedFiles.length;
- },
- commitMessageCount() {
- return this.commitMessage.length;
- },
- },
- methods: {
- ...mapActions([
- 'checkCommitStatus',
- 'commitChanges',
- 'getTreeData',
- 'setPanelCollapsedStatus',
- ]),
- makeCommit(newBranch = false) {
- const createNewBranch = newBranch || this.startNewMR;
-
- const payload = {
- branch: createNewBranch ?
- `${this.currentBranchId}-${new Date().getTime().toString()}` :
- this.currentBranchId,
- commit_message: this.commitMessage,
- actions: this.changedFiles.map(f => ({
- action: f.tempFile ? 'create' : 'update',
- file_path: f.path,
- content: f.content,
- encoding: f.base64 ? 'base64' : 'text',
- })),
- start_branch: createNewBranch ? this.currentBranchId : undefined,
- };
-
- this.showNewBranchModal = false;
- this.submitCommitsLoading = true;
-
- this.commitChanges({ payload, newMr: this.startNewMR })
- .then(() => {
- this.submitCommitsLoading = false;
- this.commitMessage = '';
- this.startNewMR = false;
- })
- .catch(() => {
- this.submitCommitsLoading = false;
- });
- },
- tryCommit() {
- this.submitCommitsLoading = true;
-
- this.checkCommitStatus()
- .then((branchChanged) => {
- if (branchChanged) {
- this.showNewBranchModal = true;
- } else {
- this.makeCommit();
- }
- })
- .catch(() => {
- this.submitCommitsLoading = false;
- });
- },
- toggleCollapsed() {
- this.setPanelCollapsedStatus({
- side: 'right',
- collapsed: !this.rightPanelCollapsed,
- });
- },
- },
-};
-</script>
-
-<template>
- <div class="multi-file-commit-panel-section">
- <modal
- v-if="showNewBranchModal"
- :primary-button-label="__('Create new branch')"
- kind="primary"
- :title="__('Branch has changed')"
- :text="__(`This branch has changed since
-you started editing. Would you like to create a new branch?`)"
- @cancel="showNewBranchModal = false"
- @submit="makeCommit(true)"
- />
- <commit-files-list
- title="Staged"
- :file-list="changedFiles"
- :collapsed="rightPanelCollapsed"
- @toggleCollapsed="toggleCollapsed"
- />
- <form
- class="form-horizontal multi-file-commit-form"
- @submit.prevent="tryCommit"
- v-if="!rightPanelCollapsed"
- >
- <div class="multi-file-commit-fieldset">
- <textarea
- class="form-control multi-file-commit-message"
- name="commit-message"
- v-model="commitMessage"
- placeholder="Commit message"
- >
- </textarea>
- </div>
- <div class="multi-file-commit-fieldset">
- <label
- v-tooltip
- title="Create a new merge request with these changes"
- data-container="body"
- data-placement="top"
- >
- <input
- type="checkbox"
- v-model="startNewMR"
- />
- Merge Request
- </label>
- <button
- type="submit"
- :disabled="commitButtonDisabled"
- class="btn btn-default btn-sm append-right-10 prepend-left-10"
- :class="{ disabled: submitCommitsLoading }"
- >
- <i
- v-if="submitCommitsLoading"
- class="js-commit-loading-icon fa fa-spinner fa-spin"
- aria-hidden="true"
- aria-label="loading"
- >
- </i>
- Commit
- </button>
- <div
- class="multi-file-commit-message-count"
- >
- {{ commitMessageCount }}
- </div>
- </div>
- </form>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue
deleted file mode 100644
index fe4320731d9..00000000000
--- a/app/assets/javascripts/ide/components/repo_edit_button.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
-import { mapGetters, mapActions, mapState } from 'vuex';
-import modal from '~/vue_shared/components/modal.vue';
-
-export default {
- components: {
- modal,
- },
- computed: {
- ...mapState([
- 'editMode',
- 'discardPopupOpen',
- ]),
- ...mapGetters([
- 'canEditFile',
- ]),
- buttonLabel() {
- return this.editMode ? this.__('Cancel edit') : this.__('Edit');
- },
- },
- methods: {
- ...mapActions([
- 'toggleEditMode',
- 'closeDiscardPopup',
- ]),
- },
-};
-</script>
-
-<template>
- <div class="editable-mode">
- <button
- v-if="canEditFile"
- class="btn btn-default"
- type="button"
- @click.prevent="toggleEditMode()">
- <i
- v-if="!editMode"
- class="fa fa-pencil"
- aria-hidden="true">
- </i>
- <span>
- {{ buttonLabel }}
- </span>
- </button>
- <modal
- v-if="discardPopupOpen"
- class="text-left"
- :primary-button-label="__('Discard changes')"
- kind="warning"
- :title="__('Are you sure?')"
- :text="__('Are you sure you want to discard your changes?')"
- @cancel="closeDiscardPopup"
- @submit="toggleEditMode(true)"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
deleted file mode 100644
index f31cc12339b..00000000000
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ /dev/null
@@ -1,136 +0,0 @@
-<script>
-/* global monaco */
-import { mapState, mapGetters, mapActions } from 'vuex';
-import flash from '~/flash';
-import monacoLoader from '../monaco_loader';
-import Editor from '../lib/editor';
-
-export default {
- computed: {
- ...mapGetters([
- 'activeFile',
- 'activeFileExtension',
- ]),
- ...mapState([
- 'leftPanelCollapsed',
- 'rightPanelCollapsed',
- 'panelResizing',
- ]),
- shouldHideEditor() {
- return this.activeFile.binary && !this.activeFile.raw;
- },
- },
- watch: {
- activeFile(oldVal, newVal) {
- if (newVal && !newVal.active) {
- this.initMonaco();
- }
- },
- leftPanelCollapsed() {
- this.editor.updateDimensions();
- },
- rightPanelCollapsed() {
- this.editor.updateDimensions();
- },
- panelResizing(isResizing) {
- if (isResizing === false) {
- this.editor.updateDimensions();
- }
- },
- },
- beforeDestroy() {
- this.editor.dispose();
- },
- mounted() {
- if (this.editor && monaco) {
- this.initMonaco();
- } else {
- monacoLoader(['vs/editor/editor.main'], () => {
- this.editor = Editor.create(monaco);
-
- this.initMonaco();
- });
- }
- },
- methods: {
- ...mapActions([
- 'getRawFileData',
- 'changeFileContent',
- 'setFileLanguage',
- 'setEditorPosition',
- 'setFileEOL',
- ]),
- initMonaco() {
- if (this.shouldHideEditor) return;
-
- this.editor.clearEditor();
-
- this.getRawFileData(this.activeFile)
- .then(() => {
- this.editor.createInstance(this.$refs.editor);
- })
- .then(() => this.setupEditor())
- .catch((err) => {
- flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
- throw err;
- });
- },
- setupEditor() {
- if (!this.activeFile) return;
-
- const model = this.editor.createModel(this.activeFile);
-
- this.editor.attachModel(model);
-
- model.onChange((m) => {
- this.changeFileContent({
- file: this.activeFile,
- content: m.getValue(),
- });
- });
-
- // Handle Cursor Position
- this.editor.onPositionChange((instance, e) => {
- this.setEditorPosition({
- editorRow: e.position.lineNumber,
- editorColumn: e.position.column,
- });
- });
-
- this.editor.setPosition({
- lineNumber: this.activeFile.editorRow,
- column: this.activeFile.editorColumn,
- });
-
- // Handle File Language
- this.setFileLanguage({
- fileLanguage: model.language,
- });
-
- // Get File eol
- this.setFileEOL({
- eol: model.eol,
- });
- },
- },
-};
-</script>
-
-<template>
- <div
- id="ide"
- class="blob-viewer-container blob-editor-container"
- >
- <div
- v-if="shouldHideEditor"
- v-html="activeFile.html"
- >
- </div>
- <div
- v-show="!shouldHideEditor"
- ref="editor"
- class="multi-file-editor-holder"
- >
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
deleted file mode 100644
index cbbab765e1c..00000000000
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ /dev/null
@@ -1,165 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import timeAgoMixin from '~/vue_shared/mixins/timeago';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
- import fileIcon from '~/vue_shared/components/file_icon.vue';
- import newDropdown from './new_dropdown/index.vue';
-
- export default {
- components: {
- skeletonLoadingContainer,
- newDropdown,
- fileIcon,
- },
- mixins: [
- timeAgoMixin,
- ],
- props: {
- file: {
- type: Object,
- required: true,
- },
- showExtraColumns: {
- type: Boolean,
- default: false,
- },
- },
- computed: {
- ...mapState([
- 'leftPanelCollapsed',
- ]),
- isSubmodule() {
- return this.file.type === 'submodule';
- },
- isTree() {
- return this.file.type === 'tree';
- },
- levelIndentation() {
- if (this.file.level > 0) {
- return {
- marginLeft: `${this.file.level * 16}px`,
- };
- }
- return {};
- },
- shortId() {
- return this.file.id.substr(0, 8);
- },
- submoduleColSpan() {
- return !this.leftPanelCollapsed && this.isSubmodule ? 3 : 1;
- },
- fileClass() {
- if (this.file.type === 'blob') {
- if (this.file.active) {
- return 'file-open file-active';
- }
- return this.file.opened ? 'file-open' : '';
- }
- return '';
- },
- changedClass() {
- return {
- 'fa-circle unsaved-icon': this.file.changed || this.file.tempFile,
- };
- },
- },
- updated() {
- if (this.file.type === 'blob' && this.file.active) {
- this.$el.scrollIntoView();
- }
- },
- methods: {
- clickFile(row) {
- // Manual Action if a tree is selected/opened
- if (this.file.type === 'tree' && this.$router.currentRoute.path === `/project${row.url}`) {
- this.$store.dispatch('toggleTreeOpen', {
- endpoint: this.file.url,
- tree: this.file,
- });
- }
- this.$router.push(`/project${row.url}`);
- },
- },
- };
-</script>
-
-<template>
- <tr
- class="file"
- :class="fileClass"
- @click="clickFile(file)">
- <td
- class="multi-file-table-name"
- :colspan="submoduleColSpan"
- >
- <a
- class="repo-file-name"
- >
- <file-icon
- :file-name="file.name"
- :loading="file.loading"
- :folder="file.type === 'tree'"
- :opened="file.opened"
- :style="levelIndentation"
- :size="16"
- />
- {{ file.name }}
- </a>
- <new-dropdown
- v-if="isTree"
- :project-id="file.projectId"
- :branch="file.branchId"
- :path="file.path"
- :parent="file"
- />
- <i
- class="fa"
- v-if="file.changed || file.tempFile"
- :class="changedClass"
- aria-hidden="true"
- >
- </i>
- <template v-if="isSubmodule && file.id">
- @
- <span class="commit-sha">
- <a
- @click.stop
- :href="file.tree_url"
- >
- {{ shortId }}
- </a>
- </span>
- </template>
- </td>
-
- <template v-if="showExtraColumns && !isSubmodule">
- <td class="multi-file-table-col-commit-message hidden-sm hidden-xs">
- <a
- v-if="file.lastCommit.message"
- @click.stop
- :href="file.lastCommit.url"
- >
- {{ file.lastCommit.message }}
- </a>
- <skeleton-loading-container
- v-else
- :small="true"
- />
- </td>
-
- <td class="commit-update hidden-xs text-right">
- <span
- v-if="file.lastCommit.updatedAt"
- :title="tooltipTitle(file.lastCommit.updatedAt)"
- >
- {{ timeFormated(file.lastCommit.updatedAt) }}
- </span>
- <skeleton-loading-container
- v-else
- class="animation-container-right"
- :small="true"
- />
- </td>
- </template>
- </tr>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_file_buttons.vue b/app/assets/javascripts/ide/components/repo_file_buttons.vue
deleted file mode 100644
index aabc0d8eada..00000000000
--- a/app/assets/javascripts/ide/components/repo_file_buttons.vue
+++ /dev/null
@@ -1,60 +0,0 @@
-<script>
-import { mapGetters } from 'vuex';
-
-export default {
- computed: {
- ...mapGetters([
- 'activeFile',
- ]),
- showButtons() {
- return this.activeFile.rawPath ||
- this.activeFile.blamePath ||
- this.activeFile.commitsPath ||
- this.activeFile.permalink;
- },
- rawDownloadButtonLabel() {
- return this.activeFile.binary ? 'Download' : 'Raw';
- },
- },
-};
-</script>
-
-<template>
- <div
- v-if="showButtons"
- class="multi-file-editor-btn-group"
- >
- <a
- :href="activeFile.rawPath"
- target="_blank"
- class="btn btn-default btn-sm raw"
- rel="noopener noreferrer">
- {{ rawDownloadButtonLabel }}
- </a>
-
- <div
- class="btn-group"
- role="group"
- aria-label="File actions"
- >
- <a
- :href="activeFile.blamePath"
- class="btn btn-default btn-sm blame"
- >
- Blame
- </a>
- <a
- :href="activeFile.commitsPath"
- class="btn btn-default btn-sm history"
- >
- History
- </a>
- <a
- :href="activeFile.permalink"
- class="btn btn-default btn-sm permalink"
- >
- Permalink
- </a>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue
deleted file mode 100644
index 79af8c0b0c7..00000000000
--- a/app/assets/javascripts/ide/components/repo_loading_file.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-
- export default {
- components: {
- skeletonLoadingContainer,
- },
- computed: {
- ...mapState([
- 'leftPanelCollapsed',
- ]),
- },
- };
-</script>
-
-<template>
- <tr
- class="loading-file"
- aria-label="Loading files"
- >
- <td class="multi-file-table-col-name">
- <skeleton-loading-container
- :small="true"
- />
- </td>
- <template v-if="!leftPanelCollapsed">
- <td class="hidden-sm hidden-xs">
- <skeleton-loading-container
- :small="true"
- />
- </td>
-
- <td class="hidden-xs">
- <skeleton-loading-container
- class="animation-container-right"
- :small="true"
- />
- </td>
- </template>
- </tr>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_prev_directory.vue b/app/assets/javascripts/ide/components/repo_prev_directory.vue
deleted file mode 100644
index 7cd359ea4ed..00000000000
--- a/app/assets/javascripts/ide/components/repo_prev_directory.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<script>
- import { mapState, mapActions } from 'vuex';
-
- export default {
- computed: {
- ...mapState([
- 'parentTreeUrl',
- 'leftPanelCollapsed',
- ]),
- colSpanCondition() {
- return this.leftPanelCollapsed ? undefined : 3;
- },
- },
- methods: {
- ...mapActions([
- 'getTreeData',
- ]),
- },
- };
-</script>
-
-<template>
- <tr class="file prev-directory">
- <td
- :colspan="colSpanCondition"
- class="table-cell"
- @click.prevent="getTreeData({ endpoint: parentTreeUrl })"
- >
- <a :href="parentTreeUrl">...</a>
- </td>
- </tr>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_preview.vue b/app/assets/javascripts/ide/components/repo_preview.vue
deleted file mode 100644
index a216269e292..00000000000
--- a/app/assets/javascripts/ide/components/repo_preview.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
- import { mapGetters } from 'vuex';
- import LineHighlighter from '~/line_highlighter';
- import syntaxHighlight from '~/syntax_highlight';
-
- export default {
- computed: {
- ...mapGetters([
- 'activeFile',
- ]),
- renderErrorTooLarge() {
- return this.activeFile.renderError === 'too_large';
- },
- },
- mounted() {
- this.highlightFile();
- this.lineHighlighter = new LineHighlighter({
- fileHolderSelector: '.blob-viewer-container',
- scrollFileHolder: true,
- });
- },
- updated() {
- this.$nextTick(() => {
- this.highlightFile();
- });
- },
- methods: {
- highlightFile() {
- syntaxHighlight($(this.$el).find('.file-content'));
- },
- },
- };
-</script>
-
-<template>
- <div>
- <div
- v-if="!activeFile.renderError"
- v-html="activeFile.html"
- class="multi-file-preview-holder"
- >
- </div>
- <div
- v-else-if="activeFile.tempFile"
- class="vertical-center render-error">
- <p class="text-center">
- The source could not be displayed for this temporary file.
- </p>
- </div>
- <div
- v-else-if="renderErrorTooLarge"
- class="vertical-center render-error">
- <p class="text-center">
- The source could not be displayed because it is too large.
- You can <a
- :href="activeFile.rawPath"
- download>download</a> it instead.
- </p>
- </div>
- <div
- v-else
- class="vertical-center render-error">
- <p class="text-center">
- The source could not be displayed because a rendering error occurred.
- You can <a
- :href="activeFile.rawPath"
- download>download</a> it instead.
- </p>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
deleted file mode 100644
index 5656081c598..00000000000
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<script>
- import { mapActions } from 'vuex';
- import fileIcon from '~/vue_shared/components/file_icon.vue';
-
- export default {
- components: {
- fileIcon,
- },
- props: {
- tab: {
- type: Object,
- required: true,
- },
- },
- computed: {
- closeLabel() {
- if (this.tab.changed || this.tab.tempFile) {
- return `${this.tab.name} changed`;
- }
- return `Close ${this.tab.name}`;
- },
- changedClass() {
- const tabChangedObj = {
- 'fa-times close-icon': !this.tab.changed && !this.tab.tempFile,
- 'fa-circle unsaved-icon': this.tab.changed || this.tab.tempFile,
- };
- return tabChangedObj;
- },
- },
-
- methods: {
- ...mapActions([
- 'closeFile',
- ]),
- clickFile(tab) {
- this.$router.push(`/project${tab.url}`);
- },
- },
- };
-</script>
-
-<template>
- <li @click="clickFile(tab)">
- <button
- type="button"
- class="multi-file-tab-close"
- @click.stop.prevent="closeFile({ file: tab })"
- :aria-label="closeLabel"
- :class="{
- 'modified': tab.changed,
- }"
- :disabled="tab.changed"
- >
- <i
- class="fa"
- :class="changedClass"
- aria-hidden="true"
- >
- </i>
- </button>
-
- <div
- class="multi-file-tab"
- :class="{active : tab.active }"
- :title="tab.url"
- >
- <file-icon
- :file-name="tab.name"
- :size="16"
- />
- {{ tab.name }}
- </div>
- </li>
-</template>
diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue
deleted file mode 100644
index ca363bba0ef..00000000000
--- a/app/assets/javascripts/ide/components/repo_tabs.vue
+++ /dev/null
@@ -1,27 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import RepoTab from './repo_tab.vue';
-
- export default {
- components: {
- 'repo-tab': RepoTab,
- },
- computed: {
- ...mapState([
- 'openFiles',
- ]),
- },
- };
-</script>
-
-<template>
- <ul
- class="multi-file-tabs list-unstyled append-bottom-0"
- >
- <repo-tab
- v-for="tab in openFiles"
- :key="tab.key"
- :tab="tab"
- />
- </ul>
-</template>
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
deleted file mode 100644
index a7fb9e0588a..00000000000
--- a/app/assets/javascripts/ide/ide_router.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import Vue from 'vue';
-import VueRouter from 'vue-router';
-import store from './stores';
-import flash from '../flash';
-import {
- getTreeEntry,
-} from './stores/utils';
-
-Vue.use(VueRouter);
-
-/**
- * Routes below /-/ide/:
-
-/project/h5bp/html5-boilerplate/blob/master
-/project/h5bp/html5-boilerplate/blob/master/app/js/test.js
-
-/project/h5bp/html5-boilerplate/mr/123
-/project/h5bp/html5-boilerplate/mr/123/app/js/test.js
-
-/workspace/123
-/workspace/project/h5bp/html5-boilerplate/blob/my-special-branch
-/workspace/project/h5bp/html5-boilerplate/mr/123
-
-/ = /workspace
-
-/settings
-*/
-
-// Unfortunately Vue Router doesn't work without at least a fake component
-// If you do only data handling
-const EmptyRouterComponent = {
- render(createElement) {
- return createElement('div');
- },
-};
-
-const router = new VueRouter({
- mode: 'history',
- base: `${gon.relative_url_root}/-/ide/`,
- routes: [
- {
- path: '/project/:namespace/:project',
- component: EmptyRouterComponent,
- children: [
- {
- path: ':targetmode/:branch/*',
- component: EmptyRouterComponent,
- },
- {
- path: 'mr/:mrid',
- component: EmptyRouterComponent,
- },
- ],
- },
- ],
-});
-
-router.beforeEach((to, from, next) => {
- if (to.params.namespace && to.params.project) {
- store.dispatch('getProjectData', {
- namespace: to.params.namespace,
- projectId: to.params.project,
- })
- .then(() => {
- const fullProjectId = `${to.params.namespace}/${to.params.project}`;
-
- if (to.params.branch) {
- store.dispatch('getBranchData', {
- projectId: fullProjectId,
- branchId: to.params.branch,
- });
-
- store.dispatch('getTreeData', {
- projectId: fullProjectId,
- branch: to.params.branch,
- endpoint: `/tree/${to.params.branch}`,
- })
- .then(() => {
- if (to.params[0]) {
- const treeEntry = getTreeEntry(store, `${to.params.namespace}/${to.params.project}/${to.params.branch}`, to.params[0]);
- if (treeEntry) {
- store.dispatch('handleTreeEntryAction', treeEntry);
- }
- }
- })
- .catch((e) => {
- flash('Error while loading the branch files. Please try again.', 'alert', document, null, false, true);
- throw e;
- });
- }
- })
- .catch((e) => {
- flash('Error while loading the project data. Please try again.', 'alert', document, null, false, true);
- throw e;
- });
- }
-
- next();
-});
-
-export default router;
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
deleted file mode 100644
index e8a19f47cee..00000000000
--- a/app/assets/javascripts/ide/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import ide from './components/ide.vue';
-import store from './stores';
-import router from './ide_router';
-import Translate from '../vue_shared/translate';
-
-function initIde(el) {
- if (!el) return null;
-
- return new Vue({
- el,
- store,
- router,
- components: {
- ide,
- },
- render(createElement) {
- return createElement('ide', {
- props: {
- emptyStateSvgPath: el.dataset.emptyStateSvgPath,
- },
- });
- },
- });
-}
-
-const ideElement = document.getElementById('ide');
-
-Vue.use(Translate);
-
-initIde(ideElement);
diff --git a/app/assets/javascripts/ide/lib/common/disposable.js b/app/assets/javascripts/ide/lib/common/disposable.js
deleted file mode 100644
index 84b29bdb600..00000000000
--- a/app/assets/javascripts/ide/lib/common/disposable.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export default class Disposable {
- constructor() {
- this.disposers = new Set();
- }
-
- add(...disposers) {
- disposers.forEach(disposer => this.disposers.add(disposer));
- }
-
- dispose() {
- this.disposers.forEach(disposer => disposer.dispose());
- this.disposers.clear();
- }
-}
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
deleted file mode 100644
index 14d9fe4771e..00000000000
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* global monaco */
-import Disposable from './disposable';
-
-export default class Model {
- constructor(monaco, file) {
- this.monaco = monaco;
- this.disposable = new Disposable();
- this.file = file;
- this.content = file.content !== '' ? file.content : file.raw;
-
- this.disposable.add(
- this.originalModel = this.monaco.editor.createModel(
- this.file.raw,
- undefined,
- new this.monaco.Uri(null, null, `original/${this.file.path}`),
- ),
- this.model = this.monaco.editor.createModel(
- this.content,
- undefined,
- new this.monaco.Uri(null, null, this.file.path),
- ),
- );
-
- this.events = new Map();
- }
-
- get url() {
- return this.model.uri.toString();
- }
-
- get language() {
- return this.model.getModeId();
- }
-
- get eol() {
- return this.model.getEOL() === '\n' ? 'LF' : 'CRLF';
- }
-
- get path() {
- return this.file.path;
- }
-
- getModel() {
- return this.model;
- }
-
- getOriginalModel() {
- return this.originalModel;
- }
-
- onChange(cb) {
- this.events.set(
- this.path,
- this.disposable.add(
- this.model.onDidChangeContent(e => cb(this.model, e)),
- ),
- );
- }
-
- dispose() {
- this.disposable.dispose();
- this.events.clear();
- }
-}
diff --git a/app/assets/javascripts/ide/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js
deleted file mode 100644
index fd462252795..00000000000
--- a/app/assets/javascripts/ide/lib/common/model_manager.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import Disposable from './disposable';
-import Model from './model';
-
-export default class ModelManager {
- constructor(monaco) {
- this.monaco = monaco;
- this.disposable = new Disposable();
- this.models = new Map();
- }
-
- hasCachedModel(path) {
- return this.models.has(path);
- }
-
- addModel(file) {
- if (this.hasCachedModel(file.path)) {
- return this.models.get(file.path);
- }
-
- const model = new Model(this.monaco, file);
- this.models.set(model.path, model);
- this.disposable.add(model);
-
- return model;
- }
-
- dispose() {
- // dispose of all the models
- this.disposable.dispose();
- this.models.clear();
- }
-}
diff --git a/app/assets/javascripts/ide/lib/decorations/controller.js b/app/assets/javascripts/ide/lib/decorations/controller.js
deleted file mode 100644
index 0954b7973c4..00000000000
--- a/app/assets/javascripts/ide/lib/decorations/controller.js
+++ /dev/null
@@ -1,43 +0,0 @@
-export default class DecorationsController {
- constructor(editor) {
- this.editor = editor;
- this.decorations = new Map();
- this.editorDecorations = new Map();
- }
-
- getAllDecorationsForModel(model) {
- if (!this.decorations.has(model.url)) return [];
-
- const modelDecorations = this.decorations.get(model.url);
- const decorations = [];
-
- modelDecorations.forEach(val => decorations.push(...val));
-
- return decorations;
- }
-
- addDecorations(model, decorationsKey, decorations) {
- const decorationMap = this.decorations.get(model.url) || new Map();
-
- decorationMap.set(decorationsKey, decorations);
-
- this.decorations.set(model.url, decorationMap);
-
- this.decorate(model);
- }
-
- decorate(model) {
- const decorations = this.getAllDecorationsForModel(model);
- const oldDecorations = this.editorDecorations.get(model.url) || [];
-
- this.editorDecorations.set(
- model.url,
- this.editor.instance.deltaDecorations(oldDecorations, decorations),
- );
- }
-
- dispose() {
- this.decorations.clear();
- this.editorDecorations.clear();
- }
-}
diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js
deleted file mode 100644
index dc0b1c95e59..00000000000
--- a/app/assets/javascripts/ide/lib/diff/controller.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/* global monaco */
-import { throttle } from 'underscore';
-import DirtyDiffWorker from './diff_worker';
-import Disposable from '../common/disposable';
-
-export const getDiffChangeType = (change) => {
- if (change.modified) {
- return 'modified';
- } else if (change.added) {
- return 'added';
- } else if (change.removed) {
- return 'removed';
- }
-
- return '';
-};
-
-export const getDecorator = change => ({
- range: new monaco.Range(
- change.lineNumber,
- 1,
- change.endLineNumber,
- 1,
- ),
- options: {
- isWholeLine: true,
- linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`,
- },
-});
-
-export default class DirtyDiffController {
- constructor(modelManager, decorationsController) {
- this.disposable = new Disposable();
- this.editorSimpleWorker = null;
- this.modelManager = modelManager;
- this.decorationsController = decorationsController;
- this.dirtyDiffWorker = new DirtyDiffWorker();
- this.throttledComputeDiff = throttle(this.computeDiff, 250);
- this.decorate = this.decorate.bind(this);
-
- this.dirtyDiffWorker.addEventListener('message', this.decorate);
- }
-
- attachModel(model) {
- model.onChange(() => this.throttledComputeDiff(model));
- }
-
- computeDiff(model) {
- this.dirtyDiffWorker.postMessage({
- path: model.path,
- originalContent: model.getOriginalModel().getValue(),
- newContent: model.getModel().getValue(),
- });
- }
-
- reDecorate(model) {
- this.decorationsController.decorate(model);
- }
-
- decorate({ data }) {
- const decorations = data.changes.map(change => getDecorator(change));
- this.decorationsController.addDecorations(data.path, 'dirtyDiff', decorations);
- }
-
- dispose() {
- this.disposable.dispose();
-
- this.dirtyDiffWorker.removeEventListener('message', this.decorate);
- this.dirtyDiffWorker.terminate();
- }
-}
diff --git a/app/assets/javascripts/ide/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js
deleted file mode 100644
index 0e37f5c4704..00000000000
--- a/app/assets/javascripts/ide/lib/diff/diff.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { diffLines } from 'diff';
-
-// eslint-disable-next-line import/prefer-default-export
-export const computeDiff = (originalContent, newContent) => {
- const changes = diffLines(originalContent, newContent);
-
- let lineNumber = 1;
- return changes.reduce((acc, change) => {
- const findOnLine = acc.find(c => c.lineNumber === lineNumber);
-
- if (findOnLine) {
- Object.assign(findOnLine, change, {
- modified: true,
- endLineNumber: (lineNumber + change.count) - 1,
- });
- } else if ('added' in change || 'removed' in change) {
- acc.push(Object.assign({}, change, {
- lineNumber,
- modified: undefined,
- endLineNumber: (lineNumber + change.count) - 1,
- }));
- }
-
- if (!change.removed) {
- lineNumber += change.count;
- }
-
- return acc;
- }, []);
-};
diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js
deleted file mode 100644
index e74c4046330..00000000000
--- a/app/assets/javascripts/ide/lib/diff/diff_worker.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { computeDiff } from './diff';
-
-self.addEventListener('message', (e) => {
- const data = e.data;
-
- self.postMessage({
- path: data.path,
- changes: computeDiff(data.originalContent, data.newContent),
- });
-});
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
deleted file mode 100644
index 51255f15658..00000000000
--- a/app/assets/javascripts/ide/lib/editor.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import _ from 'underscore';
-import DecorationsController from './decorations/controller';
-import DirtyDiffController from './diff/controller';
-import Disposable from './common/disposable';
-import ModelManager from './common/model_manager';
-import editorOptions from './editor_options';
-
-export default class Editor {
- static create(monaco) {
- this.editorInstance = new Editor(monaco);
-
- return this.editorInstance;
- }
-
- constructor(monaco) {
- this.monaco = monaco;
- this.currentModel = null;
- this.instance = null;
- this.dirtyDiffController = null;
- this.disposable = new Disposable();
-
- this.disposable.add(
- this.modelManager = new ModelManager(this.monaco),
- this.decorationsController = new DecorationsController(this),
- );
-
- this.debouncedUpdate = _.debounce(() => {
- this.updateDimensions();
- }, 200);
- window.addEventListener('resize', this.debouncedUpdate, false);
- }
-
- createInstance(domElement) {
- if (!this.instance) {
- this.disposable.add(
- this.instance = this.monaco.editor.create(domElement, {
- model: null,
- readOnly: false,
- contextmenu: true,
- scrollBeyondLastLine: false,
- minimap: {
- enabled: false,
- },
- }),
- this.dirtyDiffController = new DirtyDiffController(
- this.modelManager, this.decorationsController,
- ),
- );
- }
- }
-
- createModel(file) {
- return this.modelManager.addModel(file);
- }
-
- attachModel(model) {
- this.instance.setModel(model.getModel());
- if (this.dirtyDiffController) this.dirtyDiffController.attachModel(model);
-
- this.currentModel = model;
-
- this.instance.updateOptions(editorOptions.reduce((acc, obj) => {
- Object.keys(obj).forEach((key) => {
- Object.assign(acc, {
- [key]: obj[key](model),
- });
- });
- return acc;
- }, {}));
-
- if (this.dirtyDiffController) this.dirtyDiffController.reDecorate(model);
- }
-
- clearEditor() {
- if (this.instance) {
- this.instance.setModel(null);
- }
- }
-
- dispose() {
- this.disposable.dispose();
- window.removeEventListener('resize', this.debouncedUpdate);
-
- // dispose main monaco instance
- if (this.instance) {
- this.instance = null;
- }
- }
-
- updateDimensions() {
- this.instance.layout();
- }
-
- setPosition({ lineNumber, column }) {
- this.instance.revealPositionInCenter({
- lineNumber,
- column,
- });
- this.instance.setPosition({
- lineNumber,
- column,
- });
- }
-
- onPositionChange(cb) {
- this.disposable.add(
- this.instance.onDidChangeCursorPosition(e => cb(this.instance, e)),
- );
- }
-}
diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js
deleted file mode 100644
index 701affc466e..00000000000
--- a/app/assets/javascripts/ide/lib/editor_options.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export default [{
-}];
diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js
deleted file mode 100644
index 142a220097b..00000000000
--- a/app/assets/javascripts/ide/monaco_loader.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import monacoContext from 'monaco-editor/dev/vs/loader';
-
-monacoContext.require.config({
- paths: {
- vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
- },
-});
-
-// ignore CDN config and use local assets path for service worker which cannot be cross-domain
-const relativeRootPath = (gon && gon.relative_url_root) || '';
-const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
-window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
-
-// eslint-disable-next-line no-underscore-dangle
-window.__monaco_context__ = monacoContext;
-export default monacoContext.require;
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
deleted file mode 100644
index 1fb24e93f2e..00000000000
--- a/app/assets/javascripts/ide/services/index.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-import Api from '../../api';
-
-Vue.use(VueResource);
-
-export default {
- getTreeData(endpoint) {
- return Vue.http.get(endpoint, { params: { format: 'json' } });
- },
- getFileData(endpoint) {
- return Vue.http.get(endpoint, { params: { format: 'json' } });
- },
- getRawFileData(file) {
- if (file.tempFile) {
- return Promise.resolve(file.content);
- }
-
- if (file.raw) {
- return Promise.resolve(file.raw);
- }
-
- return Vue.http.get(file.rawPath, { params: { format: 'json' } })
- .then(res => res.text());
- },
- getProjectData(namespace, project) {
- return Api.project(`${namespace}/${project}`);
- },
- getBranchData(projectId, currentBranchId) {
- return Api.branchSingle(projectId, currentBranchId);
- },
- createBranch(projectId, payload) {
- const url = Api.buildUrl(Api.createBranchPath).replace(':id', projectId);
-
- return Vue.http.post(url, payload);
- },
- commit(projectId, payload) {
- return Api.commitMultiple(projectId, payload);
- },
- getTreeLastCommit(endpoint) {
- return Vue.http.get(endpoint, {
- params: {
- format: 'json',
- },
- });
- },
-};
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
deleted file mode 100644
index 2c690b1f635..00000000000
--- a/app/assets/javascripts/ide/stores/actions.js
+++ /dev/null
@@ -1,196 +0,0 @@
-import Vue from 'vue';
-import { visitUrl } from '~/lib/utils/url_utility';
-import flash from '~/flash';
-import service from '../services';
-import * as types from './mutation_types';
-import { stripHtml } from '../../lib/utils/text_utility';
-
-export const redirectToUrl = (_, url) => visitUrl(url);
-
-export const setInitialData = ({ commit }, data) =>
- commit(types.SET_INITIAL_DATA, data);
-
-export const closeDiscardPopup = ({ commit }) =>
- commit(types.TOGGLE_DISCARD_POPUP, false);
-
-export const discardAllChanges = ({ commit, getters, dispatch }) => {
- const changedFiles = getters.changedFiles;
-
- changedFiles.forEach((file) => {
- commit(types.DISCARD_FILE_CHANGES, file);
-
- if (file.tempFile) {
- dispatch('closeFile', { file, force: true });
- }
- });
-};
-
-export const closeAllFiles = ({ state, dispatch }) => {
- state.openFiles.forEach(file => dispatch('closeFile', { file }));
-};
-
-export const toggleEditMode = (
- { state, commit, getters, dispatch },
- force = false,
-) => {
- const changedFiles = getters.changedFiles;
-
- if (changedFiles.length && !force) {
- commit(types.TOGGLE_DISCARD_POPUP, true);
- } else {
- commit(types.TOGGLE_EDIT_MODE);
- commit(types.TOGGLE_DISCARD_POPUP, false);
- dispatch('toggleBlobView');
-
- if (!state.editMode) {
- dispatch('discardAllChanges');
- }
- }
-};
-
-export const toggleBlobView = ({ commit, state }) => {
- if (state.editMode) {
- commit(types.SET_EDIT_MODE);
- } else {
- commit(types.SET_PREVIEW_MODE);
- }
-};
-
-export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => {
- if (side === 'left') {
- commit(types.SET_LEFT_PANEL_COLLAPSED, collapsed);
- } else {
- commit(types.SET_RIGHT_PANEL_COLLAPSED, collapsed);
- }
-};
-
-export const setResizingStatus = ({ commit }, resizing) => {
- commit(types.SET_RESIZING_STATUS, resizing);
-};
-
-export const checkCommitStatus = ({ state }) =>
- service
- .getBranchData(state.currentProjectId, state.currentBranchId)
- .then(({ data }) => {
- const { id } = data.commit;
- const selectedBranch =
- state.projects[state.currentProjectId].branches[state.currentBranchId];
-
- if (selectedBranch.workingReference !== id) {
- return true;
- }
-
- return false;
- })
- .catch(() => flash('Error checking branch data. Please try again.', 'alert', document, null, false, true));
-
-export const commitChanges = (
- { commit, state, dispatch, getters },
- { payload, newMr },
-) =>
- service
- .commit(state.currentProjectId, payload)
- .then(({ data }) => {
- const { branch } = payload;
- if (!data.short_id) {
- flash(data.message, 'alert', document, null, false, true);
- return;
- }
-
- const selectedProject = state.projects[state.currentProjectId];
- const lastCommit = {
- commit_path: `${selectedProject.web_url}/commit/${data.id}`,
- commit: {
- message: data.message,
- authored_date: data.committed_date,
- },
- };
-
- let commitMsg = `Your changes have been committed. Commit ${data.short_id}`;
- if (data.stats) {
- commitMsg += ` with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`;
- }
-
- flash(
- commitMsg,
- 'notice',
- document,
- null,
- false,
- true);
- window.dispatchEvent(new Event('resize'));
-
- if (newMr) {
- dispatch('discardAllChanges');
- dispatch(
- 'redirectToUrl',
- `${selectedProject.web_url}/merge_requests/new?merge_request%5Bsource_branch%5D=${branch}`,
- );
- } else {
- commit(types.SET_BRANCH_WORKING_REFERENCE, {
- projectId: state.currentProjectId,
- branchId: state.currentBranchId,
- reference: data.id,
- });
-
- getters.changedFiles.forEach((entry) => {
- commit(types.SET_LAST_COMMIT_DATA, {
- entry,
- lastCommit,
- });
- });
-
- dispatch('discardAllChanges');
-
- window.scrollTo(0, 0);
- }
- })
- .catch((err) => {
- let errMsg = 'Error committing changes. Please try again.';
- if (err.response.data && err.response.data.message) {
- errMsg += ` (${stripHtml(err.response.data.message)})`;
- }
- flash(errMsg, 'alert', document, null, false, true);
- window.dispatchEvent(new Event('resize'));
- });
-
-export const createTempEntry = (
- { state, dispatch },
- { projectId, branchId, parent, name, type, content = '', base64 = false },
-) => {
- const selectedParent = parent || state.trees[`${projectId}/${branchId}`];
- if (type === 'tree') {
- dispatch('createTempTree', {
- projectId,
- branchId,
- parent: selectedParent,
- name,
- });
- } else if (type === 'blob') {
- dispatch('createTempFile', {
- projectId,
- branchId,
- parent: selectedParent,
- name,
- base64,
- content,
- });
- }
-};
-
-export const scrollToTab = () => {
- Vue.nextTick(() => {
- const tabs = document.getElementById('tabs');
-
- if (tabs) {
- const tabEl = tabs.querySelector('.active .repo-tab');
-
- tabEl.focus();
- }
- });
-};
-
-export * from './actions/tree';
-export * from './actions/file';
-export * from './actions/project';
-export * from './actions/branch';
diff --git a/app/assets/javascripts/ide/stores/actions/branch.js b/app/assets/javascripts/ide/stores/actions/branch.js
deleted file mode 100644
index bc6fd2d4163..00000000000
--- a/app/assets/javascripts/ide/stores/actions/branch.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import service from '../../services';
-import flash from '../../../flash';
-import * as types from '../mutation_types';
-
-export const getBranchData = (
- { commit, state, dispatch },
- { projectId, branchId, force = false } = {},
-) => new Promise((resolve, reject) => {
- if ((typeof state.projects[`${projectId}`] === 'undefined' ||
- !state.projects[`${projectId}`].branches[branchId])
- || force) {
- service.getBranchData(`${projectId}`, branchId)
- .then(({ data }) => {
- const { id } = data.commit;
- commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data });
- commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
- resolve(data);
- })
- .catch(() => {
- flash('Error loading branch data. Please try again.', 'alert', document, null, false, true);
- reject(new Error(`Branch not loaded - ${projectId}/${branchId}`));
- });
- } else {
- resolve(state.projects[`${projectId}`].branches[branchId]);
- }
-});
-
-export const createNewBranch = ({ state, commit }, branch) => service.createBranch(
- state.currentProjectId,
- {
- branch,
- ref: state.currentBranchId,
- },
-)
-.then(res => res.json())
-.then((data) => {
- const branchName = data.name;
- const url = location.href.replace(state.currentBranchId, branchName);
-
- if (this.$router) this.$router.push(url);
-
- commit(types.SET_CURRENT_BRANCH, branchName);
-});
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
deleted file mode 100644
index 670af2fb89e..00000000000
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import { normalizeHeaders } from '../../../lib/utils/common_utils';
-import flash from '../../../flash';
-import service from '../../services';
-import * as types from '../mutation_types';
-import router from '../../ide_router';
-import {
- findEntry,
- setPageTitle,
- createTemp,
- findIndexOfFile,
-} from '../utils';
-
-export const closeFile = ({ commit, state, dispatch }, { file, force = false }) => {
- if ((file.changed || file.tempFile) && !force) return;
-
- const indexOfClosedFile = findIndexOfFile(state.openFiles, file);
- const fileWasActive = file.active;
-
- commit(types.TOGGLE_FILE_OPEN, file);
- commit(types.SET_FILE_ACTIVE, { file, active: false });
-
- if (state.openFiles.length > 0 && fileWasActive) {
- const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1;
- const nextFileToOpen = state.openFiles[nextIndexToOpen];
-
- dispatch('setFileActive', nextFileToOpen);
- } else if (!state.openFiles.length) {
- router.push(`/project/${file.projectId}/tree/${file.branchId}/`);
- }
-
- dispatch('getLastCommitData');
-};
-
-export const setFileActive = ({ commit, state, getters, dispatch }, file) => {
- const currentActiveFile = getters.activeFile;
-
- if (file.active) return;
-
- if (currentActiveFile) {
- commit(types.SET_FILE_ACTIVE, { file: currentActiveFile, active: false });
- }
-
- commit(types.SET_FILE_ACTIVE, { file, active: true });
- dispatch('scrollToTab');
-
- // reset hash for line highlighting
- location.hash = '';
-
- commit(types.SET_CURRENT_PROJECT, file.projectId);
- commit(types.SET_CURRENT_BRANCH, file.branchId);
-};
-
-export const getFileData = ({ state, commit, dispatch }, file) => {
- commit(types.TOGGLE_LOADING, file);
-
- service.getFileData(file.url)
- .then((res) => {
- const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']);
-
- setPageTitle(pageTitle);
-
- return res.json();
- })
- .then((data) => {
- commit(types.SET_FILE_DATA, { data, file });
- commit(types.TOGGLE_FILE_OPEN, file);
- dispatch('setFileActive', file);
- commit(types.TOGGLE_LOADING, file);
- })
- .catch(() => {
- commit(types.TOGGLE_LOADING, file);
- flash('Error loading file data. Please try again.', 'alert', document, null, false, true);
- });
-};
-
-export const getRawFileData = ({ commit, dispatch }, file) => service.getRawFileData(file)
- .then((raw) => {
- commit(types.SET_FILE_RAW_DATA, { file, raw });
- })
- .catch(() => flash('Error loading file content. Please try again.', 'alert', document, null, false, true));
-
-export const changeFileContent = ({ commit }, { file, content }) => {
- commit(types.UPDATE_FILE_CONTENT, { file, content });
-};
-
-export const setFileLanguage = ({ state, commit }, { fileLanguage }) => {
- if (state.selectedFile) {
- commit(types.SET_FILE_LANGUAGE, { file: state.selectedFile, fileLanguage });
- }
-};
-
-export const setFileEOL = ({ state, commit }, { eol }) => {
- if (state.selectedFile) {
- commit(types.SET_FILE_EOL, { file: state.selectedFile, eol });
- }
-};
-
-export const setEditorPosition = ({ state, commit }, { editorRow, editorColumn }) => {
- if (state.selectedFile) {
- commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn });
- }
-};
-
-export const createTempFile = ({ state, commit, dispatch }, { projectId, branchId, parent, name, content = '', base64 = '' }) => {
- const path = parent.path !== undefined ? parent.path : '';
- // We need to do the replacement otherwise the web_url + file.url duplicate
- const newUrl = `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${name}`;
- const file = createTemp({
- projectId,
- branchId,
- name: name.replace(`${path}/`, ''),
- path,
- type: 'blob',
- level: parent.level !== undefined ? parent.level + 1 : 0,
- changed: true,
- content,
- base64,
- url: newUrl,
- });
-
- if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`, 'alert', document, null, false, true);
-
- commit(types.CREATE_TMP_FILE, {
- parent,
- file,
- });
- commit(types.TOGGLE_FILE_OPEN, file);
- dispatch('setFileActive', file);
-
- if (!state.editMode && !file.base64) {
- dispatch('toggleEditMode', true);
- }
-
- router.push(`/project${file.url}`);
-
- return Promise.resolve(file);
-};
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
deleted file mode 100644
index faeceb430a2..00000000000
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import service from '../../services';
-import flash from '../../../flash';
-import * as types from '../mutation_types';
-
-// eslint-disable-next-line import/prefer-default-export
-export const getProjectData = (
- { commit, state, dispatch },
- { namespace, projectId, force = false } = {},
-) => new Promise((resolve, reject) => {
- if (!state.projects[`${namespace}/${projectId}`] || force) {
- commit(types.TOGGLE_LOADING, state);
- service.getProjectData(namespace, projectId)
- .then(res => res.data)
- .then((data) => {
- commit(types.TOGGLE_LOADING, state);
- commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data });
- if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`);
- resolve(data);
- })
- .catch(() => {
- flash('Error loading project data. Please try again.', 'alert', document, null, false, true);
- reject(new Error(`Project not loaded ${namespace}/${projectId}`));
- });
- } else {
- resolve(state.projects[`${namespace}/${projectId}`]);
- }
-});
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
deleted file mode 100644
index 302ba45edee..00000000000
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ /dev/null
@@ -1,188 +0,0 @@
-import { visitUrl } from '../../../lib/utils/url_utility';
-import { normalizeHeaders } from '../../../lib/utils/common_utils';
-import flash from '../../../flash';
-import service from '../../services';
-import * as types from '../mutation_types';
-import router from '../../ide_router';
-import {
- setPageTitle,
- findEntry,
- createTemp,
- createOrMergeEntry,
-} from '../utils';
-
-export const getTreeData = (
- { commit, state, dispatch },
- { endpoint, tree = null, projectId, branch, force = false } = {},
-) => new Promise((resolve, reject) => {
- // We already have the base tree so we resolve immediately
- if (!tree && state.trees[`${projectId}/${branch}`] && !force) {
- resolve();
- } else {
- if (tree) commit(types.TOGGLE_LOADING, tree);
- const selectedProject = state.projects[projectId];
- // We are merging the web_url that we got on the project info with the endpoint
- // we got on the tree entry, as both contain the projectId, we replace it in the tree endpoint
- const completeEndpoint = selectedProject.web_url + (endpoint).replace(projectId, '');
- if (completeEndpoint && (!tree || !tree.tempFile)) {
- service.getTreeData(completeEndpoint)
- .then((res) => {
- const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']);
-
- setPageTitle(pageTitle);
-
- return res.json();
- })
- .then((data) => {
- if (!state.isInitialRoot) {
- commit(types.SET_ROOT, data.path === '/');
- }
-
- dispatch('updateDirectoryData', { data, tree, projectId, branch });
- const selectedTree = tree || state.trees[`${projectId}/${branch}`];
-
- commit(types.SET_PARENT_TREE_URL, data.parent_tree_url);
- commit(types.SET_LAST_COMMIT_URL, { tree: selectedTree, url: data.last_commit_path });
- if (tree) commit(types.TOGGLE_LOADING, selectedTree);
-
- const prevLastCommitPath = selectedTree.lastCommitPath;
- if (prevLastCommitPath !== null) {
- dispatch('getLastCommitData', selectedTree);
- }
- resolve(data);
- })
- .catch((e) => {
- flash('Error loading tree data. Please try again.', 'alert', document, null, false, true);
- if (tree) commit(types.TOGGLE_LOADING, tree);
- reject(e);
- });
- } else {
- resolve();
- }
- }
-});
-
-export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => {
- if (tree.opened) {
- // send empty data to clear the tree
- const data = { trees: [], blobs: [], submodules: [] };
-
- dispatch('updateDirectoryData', { data, tree, projectId: tree.projectId, branchId: tree.branchId });
- } else {
- dispatch('getTreeData', { endpoint, tree, projectId: tree.projectId, branch: tree.branchId });
- }
-
- commit(types.TOGGLE_TREE_OPEN, tree);
-};
-
-export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
- if (row.type === 'tree') {
- dispatch('toggleTreeOpen', {
- endpoint: row.url,
- tree: row,
- });
- } else if (row.type === 'submodule') {
- commit(types.TOGGLE_LOADING, row);
- visitUrl(row.url);
- } else if (row.type === 'blob' && row.opened) {
- dispatch('setFileActive', row);
- } else {
- dispatch('getFileData', row);
- }
-};
-
-export const createTempTree = (
- { state, commit, dispatch },
- { projectId, branchId, parent, name },
-) => {
- let selectedTree = parent;
- const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/');
-
- dirNames.forEach((dirName) => {
- const foundEntry = findEntry(selectedTree.tree, 'tree', dirName);
-
- if (!foundEntry) {
- const path = selectedTree.path !== undefined ? selectedTree.path : '';
- const tmpEntry = createTemp({
- projectId,
- branchId,
- name: dirName,
- path,
- type: 'tree',
- level: selectedTree.level !== undefined ? selectedTree.level + 1 : 0,
- tree: [],
- url: `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${dirName}`,
- });
-
- commit(types.CREATE_TMP_TREE, {
- parent: selectedTree,
- tmpEntry,
- });
- commit(types.TOGGLE_TREE_OPEN, tmpEntry);
-
- router.push(`/project${tmpEntry.url}`);
-
- selectedTree = tmpEntry;
- } else {
- selectedTree = foundEntry;
- }
- });
-};
-
-export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => {
- if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return;
-
- service.getTreeLastCommit(tree.lastCommitPath)
- .then((res) => {
- const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null;
-
- commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath });
-
- return res.json();
- })
- .then((data) => {
- data.forEach((lastCommit) => {
- const entry = findEntry(tree.tree, lastCommit.type, lastCommit.file_name);
-
- if (entry) {
- commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit });
- }
- });
-
- dispatch('getLastCommitData', tree);
- })
- .catch(() => flash('Error fetching log data.', 'alert', document, null, false, true));
-};
-
-export const updateDirectoryData = (
- { commit, state },
- { data, tree, projectId, branch },
-) => {
- if (!tree) {
- const existingTree = state.trees[`${projectId}/${branch}`];
- if (!existingTree) {
- commit(types.CREATE_TREE, { treePath: `${projectId}/${branch}` });
- }
- }
-
- const selectedTree = tree || state.trees[`${projectId}/${branch}`];
- const level = selectedTree.level !== undefined ? selectedTree.level + 1 : 0;
- const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl;
- const createEntry = (entry, type) => createOrMergeEntry({
- tree: selectedTree,
- projectId: `${projectId}`,
- branchId: branch,
- entry,
- level,
- type,
- parentTreeUrl,
- });
-
- const formattedData = [
- ...data.trees.map(t => createEntry(t, 'tree')),
- ...data.submodules.map(m => createEntry(m, 'submodule')),
- ...data.blobs.map(b => createEntry(b, 'blob')),
- ];
-
- commit(types.SET_DIRECTORY_DATA, { tree: selectedTree, data: formattedData });
-};
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
deleted file mode 100644
index 6b51ccff817..00000000000
--- a/app/assets/javascripts/ide/stores/getters.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export const changedFiles = state => state.openFiles.filter(file => file.changed);
-
-export const activeFile = state => state.openFiles.find(file => file.active) || null;
-
-export const activeFileExtension = (state) => {
- const file = activeFile(state);
- return file ? `.${file.path.split('.').pop()}` : '';
-};
-
-export const canEditFile = (state) => {
- const currentActiveFile = activeFile(state);
-
- return state.canCommit &&
- (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary);
-};
-
-export const addedFiles = state => changedFiles(state).filter(f => f.tempFile);
-
-export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile);
diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js
deleted file mode 100644
index 6ac9bfd8189..00000000000
--- a/app/assets/javascripts/ide/stores/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import state from './state';
-import * as actions from './actions';
-import * as getters from './getters';
-import mutations from './mutations';
-
-Vue.use(Vuex);
-
-export default new Vuex.Store({
- state: state(),
- actions,
- mutations,
- getters,
-});
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 69b218a5e7d..00000000000
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ /dev/null
@@ -1,46 +0,0 @@
-export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
-export const TOGGLE_LOADING = 'TOGGLE_LOADING';
-export const SET_PARENT_TREE_URL = 'SET_PARENT_TREE_URL';
-export const SET_ROOT = 'SET_ROOT';
-export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA';
-export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED';
-export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED';
-export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS';
-
-// Project Mutation Types
-export const SET_PROJECT = 'SET_PROJECT';
-export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT';
-export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN';
-
-// Branch Mutation Types
-export const SET_BRANCH = 'SET_BRANCH';
-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 CREATE_TMP_TREE = 'CREATE_TMP_TREE';
-export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL';
-export const CREATE_TREE = 'CREATE_TREE';
-
-// 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 UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
-export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE';
-export const SET_FILE_POSITION = 'SET_FILE_POSITION';
-export const SET_FILE_EOL = 'SET_FILE_EOL';
-export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES';
-export const CREATE_TMP_FILE = 'CREATE_TMP_FILE';
-
-// Viewer mutation types
-export const SET_PREVIEW_MODE = 'SET_PREVIEW_MODE';
-export const SET_EDIT_MODE = 'SET_EDIT_MODE';
-export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';
-export const TOGGLE_DISCARD_POPUP = 'TOGGLE_DISCARD_POPUP';
-
-export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH';
-
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
deleted file mode 100644
index 03d81be10a1..00000000000
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import * as types from './mutation_types';
-import projectMutations from './mutations/project';
-import fileMutations from './mutations/file';
-import treeMutations from './mutations/tree';
-import branchMutations from './mutations/branch';
-
-export default {
- [types.SET_INITIAL_DATA](state, data) {
- Object.assign(state, data);
- },
- [types.SET_PREVIEW_MODE](state) {
- Object.assign(state, {
- currentBlobView: 'repo-preview',
- });
- },
- [types.SET_EDIT_MODE](state) {
- Object.assign(state, {
- currentBlobView: 'repo-editor',
- });
- },
- [types.TOGGLE_LOADING](state, entry) {
- Object.assign(entry, {
- loading: !entry.loading,
- });
- },
- [types.TOGGLE_EDIT_MODE](state) {
- Object.assign(state, {
- editMode: !state.editMode,
- });
- },
- [types.TOGGLE_DISCARD_POPUP](state, discardPopupOpen) {
- Object.assign(state, {
- discardPopupOpen,
- });
- },
- [types.SET_ROOT](state, isRoot) {
- Object.assign(state, {
- isRoot,
- isInitialRoot: isRoot,
- });
- },
- [types.SET_LEFT_PANEL_COLLAPSED](state, collapsed) {
- Object.assign(state, {
- leftPanelCollapsed: collapsed,
- });
- },
- [types.SET_RIGHT_PANEL_COLLAPSED](state, collapsed) {
- Object.assign(state, {
- rightPanelCollapsed: collapsed,
- });
- },
- [types.SET_RESIZING_STATUS](state, resizing) {
- Object.assign(state, {
- panelResizing: resizing,
- });
- },
- [types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) {
- Object.assign(entry.lastCommit, {
- id: lastCommit.commit.id,
- url: lastCommit.commit_path,
- message: lastCommit.commit.message,
- author: lastCommit.commit.author_name,
- updatedAt: lastCommit.commit.authored_date,
- });
- },
- ...projectMutations,
- ...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 04b9582c5bb..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/branch.js
+++ /dev/null
@@ -1,28 +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 }) {
- // Add client side properties
- Object.assign(branch, {
- treeId: `${projectPath}/${branchName}`,
- active: true,
- workingReference: '',
- });
-
- Object.assign(state.projects[projectPath], {
- branches: {
- [branchName]: branch,
- },
- });
- },
- [types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) {
- Object.assign(state.projects[projectId].branches[branchId], {
- workingReference: reference,
- });
- },
-};
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 72db1c180c9..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as types from '../mutation_types';
-import { findIndexOfFile } from '../utils';
-
-export default {
- [types.SET_FILE_ACTIVE](state, { file, active }) {
- Object.assign(file, {
- active,
- });
-
- Object.assign(state, {
- selectedFile: file,
- });
- },
- [types.TOGGLE_FILE_OPEN](state, file) {
- Object.assign(file, {
- opened: !file.opened,
- });
-
- if (file.opened) {
- state.openFiles.push(file);
- } else {
- state.openFiles.splice(findIndexOfFile(state.openFiles, file), 1);
- }
- },
- [types.SET_FILE_DATA](state, { data, file }) {
- Object.assign(file, {
- blamePath: data.blame_path,
- commitsPath: data.commits_path,
- permalink: data.permalink,
- rawPath: data.raw_path,
- binary: data.binary,
- html: data.html,
- renderError: data.render_error,
- });
- },
- [types.SET_FILE_RAW_DATA](state, { file, raw }) {
- Object.assign(file, {
- raw,
- });
- },
- [types.UPDATE_FILE_CONTENT](state, { file, content }) {
- const changed = content !== file.raw;
-
- Object.assign(file, {
- content,
- changed,
- });
- },
- [types.SET_FILE_LANGUAGE](state, { file, fileLanguage }) {
- Object.assign(file, {
- fileLanguage,
- });
- },
- [types.SET_FILE_EOL](state, { file, eol }) {
- Object.assign(file, {
- eol,
- });
- },
- [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) {
- Object.assign(file, {
- editorRow,
- editorColumn,
- });
- },
- [types.DISCARD_FILE_CHANGES](state, file) {
- Object.assign(file, {
- content: file.raw,
- changed: false,
- });
- },
- [types.CREATE_TMP_FILE](state, { file, parent }) {
- parent.tree.push(file);
- },
-};
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 2816562a919..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/project.js
+++ /dev/null
@@ -1,23 +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: {},
- active: true,
- });
-
- Object.assign(state, {
- projects: Object.assign({}, 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 4fe438ab465..00000000000
--- a/app/assets/javascripts/ide/stores/mutations/tree.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as types from '../mutation_types';
-
-export default {
- [types.TOGGLE_TREE_OPEN](state, tree) {
- Object.assign(tree, {
- opened: !tree.opened,
- });
- },
- [types.CREATE_TREE](state, { treePath }) {
- Object.assign(state, {
- trees: Object.assign({}, state.trees, {
- [treePath]: {
- tree: [],
- },
- }),
- });
- },
- [types.SET_DIRECTORY_DATA](state, { data, tree }) {
- Object.assign(tree, {
- tree: data,
- });
- },
- [types.SET_PARENT_TREE_URL](state, url) {
- Object.assign(state, {
- parentTreeUrl: url,
- });
- },
- [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
- Object.assign(tree, {
- lastCommitPath: url,
- });
- },
- [types.CREATE_TMP_TREE](state, { parent, tmpEntry }) {
- parent.tree.push(tmpEntry);
- },
-};
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
deleted file mode 100644
index 61d12096946..00000000000
--- a/app/assets/javascripts/ide/stores/state.js
+++ /dev/null
@@ -1,23 +0,0 @@
-export default () => ({
- canCommit: false,
- currentProjectId: '',
- currentBranchId: '',
- currentBlobView: 'repo-editor',
- discardPopupOpen: false,
- editMode: true,
- endpoints: {},
- isRoot: false,
- isInitialRoot: false,
- lastCommitPath: '',
- loading: false,
- onTopOfBranch: false,
- openFiles: [],
- selectedFile: null,
- path: '',
- parentTreeUrl: '',
- trees: {},
- projects: {},
- leftPanelCollapsed: false,
- rightPanelCollapsed: true,
- panelResizing: false,
-});
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
deleted file mode 100644
index d556404faa5..00000000000
--- a/app/assets/javascripts/ide/stores/utils.js
+++ /dev/null
@@ -1,177 +0,0 @@
-import _ from 'underscore';
-
-export const dataStructure = () => ({
- id: '',
- key: '',
- type: '',
- projectId: '',
- branchId: '',
- name: '',
- url: '',
- path: '',
- level: 0,
- tempFile: false,
- icon: '',
- tree: [],
- loading: false,
- opened: false,
- active: false,
- changed: false,
- lastCommitPath: '',
- lastCommit: {
- id: '',
- url: '',
- message: '',
- updatedAt: '',
- author: '',
- },
- tree_url: '',
- blamePath: '',
- commitsPath: '',
- permalink: '',
- rawPath: '',
- binary: false,
- html: '',
- raw: '',
- content: '',
- parentTreeUrl: '',
- renderError: false,
- base64: false,
- editorRow: 1,
- editorColumn: 1,
- fileLanguage: '',
- eol: '',
-});
-
-export const decorateData = (entity) => {
- const {
- id,
- projectId,
- branchId,
- type,
- url,
- name,
- icon,
- tree_url,
- path,
- renderError,
- content = '',
- tempFile = false,
- active = false,
- opened = false,
- changed = false,
- parentTreeUrl = '',
- level = 0,
- base64 = false,
- } = entity;
-
- return {
- ...dataStructure(),
- id,
- projectId,
- branchId,
- key: `${name}-${type}-${id}`,
- type,
- name,
- url,
- tree_url,
- path,
- level,
- tempFile,
- icon: `fa-${icon}`,
- opened,
- active,
- parentTreeUrl,
- changed,
- renderError,
- content,
- base64,
- };
-};
-
-/*
- Takes the multi-dimensional tree and returns a flattened array.
- This allows for the table to recursively render the table rows but keeps the data
- structure nested to make it easier to add new files/directories.
-*/
-export const treeList = (state, treeId) => {
- const baseTree = state.trees[treeId];
- if (baseTree) {
- const mapTree = arr => (!arr.tree || !arr.tree.length ?
- [] : _.map(arr.tree, a => [a, mapTree(a)]));
-
- return _.chain(baseTree.tree)
- .map(arr => [arr, mapTree(arr)])
- .flatten()
- .value();
- }
- return [];
-};
-
-export const getTree = state => (namespace, projectId, branch) => state.trees[`${namespace}/${projectId}/${branch}`];
-
-export const getTreeEntry = (store, treeId, path) => {
- const fileList = treeList(store.state, treeId);
- return fileList ? fileList.find(file => file.path === path) : null;
-};
-
-export const findEntry = (tree, type, name) => tree.find(
- f => f.type === type && f.name === name,
-);
-
-export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path);
-
-export const setPageTitle = (title) => {
- document.title = title;
-};
-
-export const createTemp = ({
- projectId, branchId, name, path, type, level, changed, content, base64, url,
-}) => {
- const treePath = path ? `${path}/${name}` : name;
-
- return decorateData({
- id: new Date().getTime().toString(),
- projectId,
- branchId,
- name,
- type,
- tempFile: true,
- path: treePath,
- icon: type === 'tree' ? 'folder' : 'file-text-o',
- changed,
- content,
- parentTreeUrl: '',
- level,
- base64,
- renderError: base64,
- url,
- });
-};
-
-export const createOrMergeEntry = ({ tree,
- projectId,
- branchId,
- entry,
- type,
- parentTreeUrl,
- level }) => {
- const found = findEntry(tree.tree || tree, type, entry.name);
-
- if (found) {
- return Object.assign({}, found, {
- id: entry.id,
- url: entry.url,
- tempFile: false,
- });
- }
-
- return decorateData({
- ...entry,
- projectId,
- branchId,
- type,
- parentTreeUrl,
- level,
- });
-};
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
deleted file mode 100644
index 1ff25a45398..00000000000
--- a/app/controllers/ide_controller.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class IdeController < ApplicationController
- layout 'nav_only'
-
- def index
- end
-end
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index edb17843002..47c8b9b60ed 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -110,10 +110,6 @@ class TodosFinder
ids
end
- def projects(items)
- ProjectsFinder.new(current_user: current_user, project_ids_relation: project_ids(items)).execute
- end
-
def type?
type.present? && %w(Issue MergeRequest).include?(type)
end
@@ -152,13 +148,14 @@ class TodosFinder
def by_project(items)
if project?
- items = items.where(project: project)
+ items.where(project: project)
else
- item_projects = projects(items)
- items = items.merge(item_projects).joins(:project)
- end
+ projects = Project
+ .public_or_visible_to_user(current_user)
+ .order_id_desc
- items
+ items.joins(:project).merge(projects)
+ end
end
def by_state(items)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 475341cf9b1..af9c8bf1bd3 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -320,10 +320,6 @@ module ApplicationHelper
cookies["sidebar_collapsed"] == "true"
end
- def show_new_ide?
- cookies["new_repo"] == "true" && body_data_page != 'projects:show'
- end
-
def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 0e806d16bc5..5ff09b23a78 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -33,20 +33,6 @@ module BlobHelper
ref)
end
- def ide_edit_button(project = @project, ref = @ref, path = @path, options = {})
- return unless show_new_ide?
- return unless blob = readable_blob(options, path, project, ref)
-
- common_classes = "btn js-edit-ide #{options[:extra_class]}"
-
- edit_button_tag(blob,
- common_classes,
- _('Web IDE'),
- ide_edit_path(project, ref, path, options),
- project,
- ref)
- end
-
def modify_file_button(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
return unless current_user
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index e86e43b5ebf..a70e73a6da9 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -11,7 +11,7 @@ module NotesHelper
end
def note_supports_quick_actions?(note)
- Notes::QuickActionsService.supported?(note, current_user)
+ Notes::QuickActionsService.supported?(note)
end
def noteable_json(noteable)
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index fc586fa216e..b444812a4cf 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -15,4 +15,8 @@ class LfsObject < ActiveRecord::Base
.where(lfs_objects_projects: { id: nil })
.destroy_all
end
+
+ def self.calculate_oid(path)
+ Digest::SHA256.file(path).hexdigest
+ end
end
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index a8d0cc15527..0a33d5f3f3d 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -9,14 +9,12 @@ module Notes
UPDATE_SERVICES[note.noteable_type]
end
- def self.supported?(note, current_user)
- noteable_update_service(note) &&
- current_user &&
- current_user.can?(:"update_#{note.to_ability_name}", note.noteable)
+ def self.supported?(note)
+ !!noteable_update_service(note)
end
def supported?(note)
- self.class.supported?(note, current_user)
+ self.class.supported?(note)
end
def extract_commands(note, options = {})
diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml
deleted file mode 100644
index 3dbdfc97654..00000000000
--- a/app/views/ide/index.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-- @body_class = 'ide'
-- page_title 'IDE'
-
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'ide', force_same_domain: true
-
-#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg')} }
- .text-center
- = icon('spinner spin 2x')
- %h2.clgray= _('Loading the GitLab IDE...')
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 1b150ec3e5c..f93bb02acb9 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -12,7 +12,6 @@
.btn-group{ role: "group" }<
= edit_blob_button
- = ide_edit_button
- if current_user
= replace_blob_link
= delete_blob_link
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 39511435508..06bce52e709 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -72,11 +72,6 @@
#{ _('New tag') }
.tree-controls
- - if show_new_ide?
- = succeed " " do
- = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do
- = _('Web IDE')
-
= link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn'
= render 'projects/find_file_link'
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index 479bd2cdb38..4c8c92d722a 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,6 +1,5 @@
- show_create = local_assigns.fetch(:show_create, false)
-- show_new_branch_form = show_new_ide? && show_create && can?(current_user, :push_code, @project)
- dropdown_toggle_text = @ref || @project.default_branch
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= hidden_field_tag :destination, destination
@@ -16,14 +15,3 @@
= dropdown_filter _("Search branches and tags")
= dropdown_content
= dropdown_loading
- - if show_new_branch_form
- = dropdown_footer do
- %ul.dropdown-footer-list
- %li
- %a.dropdown-toggle-page{ href: "#" }
- Create new branch
- - if show_new_branch_form
- .dropdown-page-two
- = dropdown_title("Create new branch", options: { back: true })
- = dropdown_content do
- .js-new-branch-dropdown
diff --git a/changelogs/unreleased/43334-reply-by-email-did-not-pick-up-unsubscribe-quick-action.yml b/changelogs/unreleased/43334-reply-by-email-did-not-pick-up-unsubscribe-quick-action.yml
new file mode 100644
index 00000000000..86be5ee1804
--- /dev/null
+++ b/changelogs/unreleased/43334-reply-by-email-did-not-pick-up-unsubscribe-quick-action.yml
@@ -0,0 +1,5 @@
+---
+title: Fix quick actions for users who cannot update issues and merge requests
+merge_request: 17482
+author:
+type: fixed
diff --git a/changelogs/unreleased/ee-4862-verify-file-checksums.yml b/changelogs/unreleased/ee-4862-verify-file-checksums.yml
new file mode 100644
index 00000000000..392c766ab37
--- /dev/null
+++ b/changelogs/unreleased/ee-4862-verify-file-checksums.yml
@@ -0,0 +1,5 @@
+---
+title: Foreground verification of uploads and LFS objects
+merge_request: 17402
+author:
+type: added
diff --git a/changelogs/unreleased/remove-projects-finder-from-todos-finder.yml b/changelogs/unreleased/remove-projects-finder-from-todos-finder.yml
new file mode 100644
index 00000000000..0a3fc751edb
--- /dev/null
+++ b/changelogs/unreleased/remove-projects-finder-from-todos-finder.yml
@@ -0,0 +1,5 @@
+---
+title: Don't use ProjectsFinder in TodosFinder
+merge_request:
+author:
+type: performance
diff --git a/config/routes.rb b/config/routes.rb
index e72ea1881cd..35fd76fb119 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -43,8 +43,6 @@ Rails.application.routes.draw do
get 'liveness' => 'health#liveness'
get 'readiness' => 'health#readiness'
post 'storage_check' => 'health#storage_check'
- get 'ide' => 'ide#index'
- get 'ide/*vueroute' => 'ide#index', format: false
resources :metrics, only: [:index]
mount Peek::Railtie => '/peek'
diff --git a/config/webpack.config.js b/config/webpack.config.js
index cc098c8a3f9..19eeb497a14 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -47,7 +47,6 @@ function generateEntries() {
common_vue: './vue_shared/vue_resource_interceptor.js',
locale: './locale/index.js',
main: './main.js',
- ide: './ide/index.js',
raven: './raven/index.js',
webpack_runtime: './webpack.js',
};
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index d1ed152b58c..d73d9422d2c 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -78,34 +78,41 @@ Example output:
## Uploaded Files Integrity
-The uploads check Rake task will loop through all uploads in the database
-and run two checks to determine the integrity of each file:
+Various types of file can be uploaded to a GitLab installation by users.
+Checksums are generated and stored in the database upon upload, and integrity
+checks using those checksums can be run. These checks also detect missing files.
-1. Check if the file exist on the file system.
-1. Check if the checksum of the file on the file system matches the checksum in the database.
+Currently, integrity checks are supported for the following types of file:
+
+* LFS objects
+* User uploads
**Omnibus Installation**
```
+sudo gitlab-rake gitlab:lfs:check
sudo gitlab-rake gitlab:uploads:check
```
**Source Installation**
```bash
+sudo -u git -H bundle exec rake gitlab:lfs:check RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:uploads:check RAILS_ENV=production
```
-This task also accepts some environment variables which you can use to override
+These tasks also accept some environment variables which you can use to override
certain values:
-Variable | Type | Description
--------- | ---- | -----------
-`BATCH` | integer | Specifies the size of the batch. Defaults to 200.
-`ID_FROM` | integer | Specifies the ID to start from, inclusive of the value.
-`ID_TO` | integer | Specifies the ID value to end at, inclusive of the value.
+Variable | Type | Description
+--------- | ------- | -----------
+`BATCH` | integer | Specifies the size of the batch. Defaults to 200.
+`ID_FROM` | integer | Specifies the ID to start from, inclusive of the value.
+`ID_TO` | integer | Specifies the ID value to end at, inclusive of the value.
+`VERBOSE` | boolean | Causes failures to be listed individually, rather than being summarized.
```bash
+sudo gitlab-rake gitlab:lfs:check BATCH=100 ID_FROM=50 ID_TO=250
sudo gitlab-rake gitlab:uploads:check BATCH=100 ID_FROM=50 ID_TO=250
```
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index 50eb8005b44..32f392f1303 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -53,3 +53,38 @@ bundle exec rails db RAILS_ENV=development
- `CREATE TABLE board_labels();`: Create a table called `board_labels`
- `SELECT * FROM schema_migrations WHERE version = '20170926203418';`: Check if a migration was run
- `DELETE FROM schema_migrations WHERE version = '20170926203418';`: Manually remove a migration
+
+
+## FAQ
+
+### `ActiveRecord::PendingMigrationError` with Spring
+
+When running specs with the [Spring preloader](./rake_tasks.md#speed-up-tests-rake-tasks-and-migrations),
+the test database can get into a corrupted state. Trying to run the migration or
+dropping/resetting the test database has no effect.
+
+```sh
+$ bundle exec spring rspec some_spec.rb
+...
+Failure/Error: ActiveRecord::Migration.maintain_test_schema!
+
+ActiveRecord::PendingMigrationError:
+
+
+ Migrations are pending. To resolve this issue, run:
+
+ bin/rake db:migrate RAILS_ENV=test
+# ~/.rvm/gems/ruby-2.3.3/gems/activerecord-4.2.10/lib/active_record/migration.rb:392:in `check_pending!'
+...
+0 examples, 0 failures, 1 error occurred outside of examples
+```
+
+To resolve, you can kill the spring server and app that lives between spec runs.
+
+```sh
+$ ps aux | grep spring
+eric 87304 1.3 2.9 3080836 482596 ?? Ss 10:12AM 4:08.36 spring app | gitlab | started 6 hours ago | test mode
+eric 37709 0.0 0.0 2518640 7524 s006 S Wed11AM 0:00.79 spring server | gitlab | started 29 hours ago
+$ kill 87304
+$ kill 37709
+```
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 9aa3fb07abf..b732cc65b73 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -22,6 +22,8 @@ are very appreciative of the work done by translators and proofreaders!
- Japanese
- Korean
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+- Polish
+ - Filip Mech - [GitLab](https://gitlab.com/mehenz), [Crowdin](https://crowdin.com/profile/mehenz)
- Portuguese, Brazilian
- Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [Crowdin](https://crowdin.com/profile/paulogomes.rep)
- Russian
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index dc88ce1522c..fdfa1f10402 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -102,6 +102,12 @@ variable to `1`:
export ENABLE_SPRING=1
```
+Alternatively you can use the following on each spec run,
+
+```
+bundle exec spring rspec some_spec.rb
+```
+
## Compile Frontend Assets
You shouldn't ever need to compile frontend assets manually in development, but
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 33a2d7a88a7..aa14a39e4c9 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -35,8 +35,8 @@ to clipboard step.
If you don't see the string or would like to generate a SSH key pair with a
custom name continue onto the next step.
->
-**Note:** Public SSH key may also be named as follows:
+Note that Public SSH key may also be named as follows:
+
- `id_dsa.pub`
- `id_ecdsa.pub`
- `id_ed25519.pub`
@@ -73,7 +73,7 @@ custom name continue onto the next step.
key pair, but it is not required and you can skip creating a password by
pressing enter.
- >**Note:**
+ NOTE: **Note:**
If you want to change the password of your SSH key pair, you can use
`ssh-keygen -p <keyname>`.
@@ -162,11 +162,13 @@ That's why it needs to uniquely map to a single user.
## Deploy keys
+### Per-repository deploy keys
+
Deploy keys allow read-only or read-write (if enabled) access to one or
multiple projects with a single SSH key pair.
This is really useful for cloning repositories to your Continuous
-Integration (CI) server. By using deploy keys, you don't have to setup a
+Integration (CI) server. By using deploy keys, you don't have to set up a
dummy user account.
If you are a project master or owner, you can add a deploy key in the
@@ -185,6 +187,47 @@ a group.
Deploy keys can be shared between projects, you just need to add them to each
project.
+### Global shared deploy keys
+
+Global Shared Deploy keys allow read-only or read-write (if enabled) access to
+be configured on any repository in the entire GitLab installation.
+
+This is really useful for integrating repositories to secured, shared Continuous
+Integration (CI) services or other shared services.
+GitLab administrators can set up the Global Shared Deploy key in GitLab and
+add the private key to any shared systems. Individual repositories opt into
+exposing their repsitory using these keys when a project masters (or higher)
+authorizes a Global Shared Deploy key to be used with their project.
+
+Global Shared Keys can provide greater security compared to Per-Project Deploy
+Keys since an administrator of the target integrated system is the only one
+who needs to know and configure the private key.
+
+GitLab administrators set up Global Deploy keys in the Admin area under the
+section **Deploy Keys**. Ensure keys have a meaningful title as that will be
+the primary way for project masters and owners to identify the correct Global
+Deploy key to add. For instance, if the key gives access to a SaaS CI instance,
+use the name of that service in the key name if that is all it is used for.
+When creating Global Shared Deploy keys, give some thought to the granularity
+of keys - they could be of very narrow usage such as just a specific service or
+of broader usage for something like "Anywhere you need to give read access to
+your repository".
+
+Once a GitLab administrator adds the Global Deployment key, project masters
+and owners can add it in project's **Settings > Repository** section by expanding the
+**Deploy Key** section and clicking **Enable** next to the appropriate key listed
+under **Public deploy keys available to any project**.
+
+NOTE: **Note:**
+The heading **Public deploy keys available to any project** only appears
+if there is at least one Global Deploy Key configured.
+
+CAUTION: **Warning:**
+Defining Global Deploy Keys does not expose any given repository via
+the key until that respository adds the Global Deploy Key to their project.
+In this way the Global Deploy Keys enable access by other systems, but do
+not implicitly give any access just by setting them up.
+
## Applications
### Eclipse
diff --git a/lib/gitlab/ci/pipeline/chain/create.rb b/lib/gitlab/ci/pipeline/chain/create.rb
index d19a2519803..d5e17a123df 100644
--- a/lib/gitlab/ci/pipeline/chain/create.rb
+++ b/lib/gitlab/ci/pipeline/chain/create.rb
@@ -17,27 +17,11 @@ module Gitlab
end
rescue ActiveRecord::RecordInvalid => e
error("Failed to persist the pipeline: #{e}")
- ensure
- if pipeline.builds.where(stage_id: nil).any?
- invalid_builds_counter.increment(node: hostname)
- end
end
def break?
!pipeline.persisted?
end
-
- private
-
- def invalid_builds_counter
- @counter ||= Gitlab::Metrics
- .counter(:gitlab_ci_invalid_builds_total,
- 'Invalid builds without stage assigned counter')
- end
-
- def hostname
- @hostname ||= Socket.gethostname
- end
end
end
end
diff --git a/lib/gitlab/verify/batch_verifier.rb b/lib/gitlab/verify/batch_verifier.rb
new file mode 100644
index 00000000000..1ef369a4b67
--- /dev/null
+++ b/lib/gitlab/verify/batch_verifier.rb
@@ -0,0 +1,64 @@
+module Gitlab
+ module Verify
+ class BatchVerifier
+ attr_reader :batch_size, :start, :finish
+
+ def initialize(batch_size:, start: nil, finish: nil)
+ @batch_size = batch_size
+ @start = start
+ @finish = finish
+ end
+
+ # Yields a Range of IDs and a Hash of failed verifications (object => error)
+ def run_batches(&blk)
+ relation.in_batches(of: batch_size, start: start, finish: finish) do |relation| # rubocop: disable Cop/InBatches
+ range = relation.first.id..relation.last.id
+ failures = run_batch(relation)
+
+ yield(range, failures)
+ end
+ end
+
+ def name
+ raise NotImplementedError.new
+ end
+
+ def describe(_object)
+ raise NotImplementedError.new
+ end
+
+ private
+
+ def run_batch(relation)
+ relation.map { |upload| verify(upload) }.compact.to_h
+ end
+
+ def verify(object)
+ expected = expected_checksum(object)
+ actual = actual_checksum(object)
+
+ raise 'Checksum missing' unless expected.present?
+ raise 'Checksum mismatch' unless expected == actual
+
+ nil
+ rescue => err
+ [object, err]
+ end
+
+ # This should return an ActiveRecord::Relation suitable for calling #in_batches on
+ def relation
+ raise NotImplementedError.new
+ end
+
+ # The checksum we expect the object to have
+ def expected_checksum(_object)
+ raise NotImplementedError.new
+ end
+
+ # The freshly-recalculated checksum of the object
+ def actual_checksum(_object)
+ raise NotImplementedError.new
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/verify/lfs_objects.rb b/lib/gitlab/verify/lfs_objects.rb
new file mode 100644
index 00000000000..fe51edbdeeb
--- /dev/null
+++ b/lib/gitlab/verify/lfs_objects.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ module Verify
+ class LfsObjects < BatchVerifier
+ def name
+ 'LFS objects'
+ end
+
+ def describe(object)
+ "LFS object: #{object.oid}"
+ end
+
+ private
+
+ def relation
+ LfsObject.all
+ end
+
+ def expected_checksum(lfs_object)
+ lfs_object.oid
+ end
+
+ def actual_checksum(lfs_object)
+ LfsObject.calculate_oid(lfs_object.file.path)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/verify/rake_task.rb b/lib/gitlab/verify/rake_task.rb
new file mode 100644
index 00000000000..dd138e6b92b
--- /dev/null
+++ b/lib/gitlab/verify/rake_task.rb
@@ -0,0 +1,53 @@
+module Gitlab
+ module Verify
+ class RakeTask
+ def self.run!(verify_kls)
+ verifier = verify_kls.new(
+ batch_size: ENV.fetch('BATCH', 200).to_i,
+ start: ENV['ID_FROM'],
+ finish: ENV['ID_TO']
+ )
+
+ verbose = Gitlab::Utils.to_boolean(ENV['VERBOSE'])
+
+ new(verifier, verbose).run!
+ end
+
+ attr_reader :verifier, :output
+
+ def initialize(verifier, verbose)
+ @verifier = verifier
+ @verbose = verbose
+ end
+
+ def run!
+ say "Checking integrity of #{verifier.name}"
+
+ verifier.run_batches { |*args| run_batch(*args) }
+
+ say 'Done!'
+ end
+
+ def verbose?
+ !!@verbose
+ end
+
+ private
+
+ def say(text)
+ puts(text) # rubocop:disable Rails/Output
+ end
+
+ def run_batch(range, failures)
+ status_color = failures.empty? ? :green : :red
+ say "- #{range}: Failures: #{failures.count}".color(status_color)
+
+ return unless verbose?
+
+ failures.each do |object, error|
+ say " - #{verifier.describe(object)}: #{error.inspect}".color(:red)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/verify/uploads.rb b/lib/gitlab/verify/uploads.rb
new file mode 100644
index 00000000000..6972e517ea5
--- /dev/null
+++ b/lib/gitlab/verify/uploads.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ module Verify
+ class Uploads < BatchVerifier
+ def name
+ 'Uploads'
+ end
+
+ def describe(object)
+ "Upload: #{object.id}"
+ end
+
+ private
+
+ def relation
+ Upload.all
+ end
+
+ def expected_checksum(upload)
+ upload.checksum
+ end
+
+ def actual_checksum(upload)
+ Upload.hexdigest(upload.absolute_path)
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/lfs/check.rake b/lib/tasks/gitlab/lfs/check.rake
new file mode 100644
index 00000000000..869463d4e5d
--- /dev/null
+++ b/lib/tasks/gitlab/lfs/check.rake
@@ -0,0 +1,8 @@
+namespace :gitlab do
+ namespace :lfs do
+ desc 'GitLab | LFS | Check integrity of uploaded LFS objects'
+ task check: :environment do
+ Gitlab::Verify::RakeTask.run!(Gitlab::Verify::LfsObjects)
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/uploads.rake b/lib/tasks/gitlab/uploads.rake
deleted file mode 100644
index df31567ce64..00000000000
--- a/lib/tasks/gitlab/uploads.rake
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace :gitlab do
- namespace :uploads do
- desc 'GitLab | Uploads | Check integrity of uploaded files'
- task check: :environment do
- puts 'Checking integrity of uploaded files'
-
- uploads_batches do |batch|
- batch.each do |upload|
- puts "- Checking file (#{upload.id}): #{upload.absolute_path}".color(:green)
-
- if upload.exist?
- check_checksum(upload)
- else
- puts " * File does not exist on the file system".color(:red)
- end
- end
- end
-
- puts 'Done!'
- end
-
- def batch_size
- ENV.fetch('BATCH', 200).to_i
- end
-
- def calculate_checksum(absolute_path)
- Digest::SHA256.file(absolute_path).hexdigest
- end
-
- def check_checksum(upload)
- checksum = calculate_checksum(upload.absolute_path)
-
- if checksum != upload.checksum
- puts " * File checksum (#{checksum}) does not match the one in the database (#{upload.checksum})".color(:red)
- end
- end
-
- def uploads_batches(&block)
- Upload.all.in_batches(of: batch_size, start: ENV['ID_FROM'], finish: ENV['ID_TO']) do |relation| # rubocop: disable Cop/InBatches
- yield relation
- end
- end
- end
-end
diff --git a/lib/tasks/gitlab/uploads/check.rake b/lib/tasks/gitlab/uploads/check.rake
new file mode 100644
index 00000000000..2be2ec7f9c9
--- /dev/null
+++ b/lib/tasks/gitlab/uploads/check.rake
@@ -0,0 +1,8 @@
+namespace :gitlab do
+ namespace :uploads do
+ desc 'GitLab | Uploads | Check integrity of uploaded files'
+ task check: :environment do
+ Gitlab::Verify::RakeTask.run!(Gitlab::Verify::Uploads)
+ end
+ end
+end
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index 8eb709022ce..caaed4d5246 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -9,4 +9,10 @@ FactoryBot.define do
trait :with_file do
file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
end
+
+ # The uniqueness constraint means we can't use the correct OID for all LFS
+ # objects, so the test needs to decide which (if any) object gets it
+ trait :correct_oid do
+ oid 'b804383982bb89b00e828e3f44c038cc991d3d1768009fc39ba8e2c081b9fb75'
+ end
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index e711a191db2..ea7a97d02a0 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -59,7 +59,6 @@ feature 'Issues > User uses quick actions', :js do
it 'does not create a note, and sets the due date accordingly' do
write_note("/due 2016-08-28")
- expect(page).to have_content '/due 2016-08-28'
expect(page).not_to have_content 'Commands applied'
issue.reload
@@ -99,7 +98,6 @@ feature 'Issues > User uses quick actions', :js do
it 'does not create a note, and sets the due date accordingly' do
write_note("/remove_due_date")
- expect(page).to have_content '/remove_due_date'
expect(page).not_to have_content 'Commands applied'
issue.reload
@@ -147,7 +145,6 @@ feature 'Issues > User uses quick actions', :js do
it 'does not create a note, and does not mark the issue as a duplicate' do
write_note("/duplicate ##{original_issue.to_reference}")
- expect(page).to have_content "/duplicate ##{original_issue.to_reference}"
expect(page).not_to have_content 'Commands applied'
expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
deleted file mode 100644
index 0c67196f53e..00000000000
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'spec_helper'
-
-feature 'Multi-file editor new directory', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
-
- before do
- project.add_master(user)
- sign_in(user)
-
- set_cookie('new_repo', 'true')
-
- visit project_tree_path(project, :master)
-
- wait_for_requests
-
- click_link('Web IDE')
-
- wait_for_requests
- end
-
- after do
- set_cookie('new_repo', 'false')
- end
-
- it 'creates directory in current directory' do
- find('.add-to-tree').click
-
- click_link('New directory')
-
- page.within('.modal') do
- find('.form-control').set('folder name')
-
- click_button('Create directory')
- end
-
- find('.add-to-tree').click
-
- click_link('New file')
-
- page.within('.modal-dialog') do
- find('.form-control').set('file name')
-
- click_button('Create file')
- end
-
- wait_for_requests
-
- find('.multi-file-commit-panel-collapse-btn').click
-
- fill_in('commit-message', with: 'commit message ide')
-
- click_button('Commit')
-
- expect(page).to have_content('folder name')
- end
-end
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
deleted file mode 100644
index 85f7318c05d..00000000000
--- a/spec/features/projects/tree/create_file_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'spec_helper'
-
-feature 'Multi-file editor new file', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
-
- before do
- project.add_master(user)
- sign_in(user)
-
- set_cookie('new_repo', 'true')
-
- visit project_tree_path(project, :master)
-
- wait_for_requests
-
- click_link('Web IDE')
-
- wait_for_requests
- end
-
- after do
- set_cookie('new_repo', 'false')
- end
-
- it 'creates file in current directory' do
- find('.add-to-tree').click
-
- click_link('New file')
-
- page.within('.modal') do
- find('.form-control').set('file name')
-
- click_button('Create file')
- end
-
- wait_for_requests
-
- find('.multi-file-commit-panel-collapse-btn').click
-
- fill_in('commit-message', with: 'commit message ide')
-
- click_button('Commit')
-
- expect(page).to have_content('file name')
- end
-end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
deleted file mode 100644
index f81e8677e92..00000000000
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'spec_helper'
-
-feature 'Multi-file editor upload file', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:txt_file) { File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') }
- let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
-
- before do
- project.add_master(user)
- sign_in(user)
-
- set_cookie('new_repo', 'true')
-
- visit project_tree_path(project, :master)
-
- wait_for_requests
-
- click_link('Web IDE')
-
- wait_for_requests
- end
-
- after do
- set_cookie('new_repo', 'false')
- end
-
- it 'uploads text file' do
- find('.add-to-tree').click
-
- # make the field visible so capybara can use it
- execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
- attach_file('file-upload', txt_file)
-
- find('.add-to-tree').click
-
- expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
- expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
- end
-
- it 'uploads image file' do
- find('.add-to-tree').click
-
- # make the field visible so capybara can use it
- execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
- attach_file('file-upload', img_file)
-
- find('.add-to-tree').click
-
- expect(page).to have_selector('.multi-file-tab', text: 'dk.png')
- expect(page).not_to have_selector('.monaco-editor')
- end
-end
diff --git a/spec/fixtures/emails/update_commands_only_reply.eml b/spec/fixtures/emails/update_commands_only_reply.eml
new file mode 100644
index 00000000000..bb0d2b0e03a
--- /dev/null
+++ b/spec/fixtures/emails/update_commands_only_reply.eml
@@ -0,0 +1,38 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+In-Reply-To: <issue_1@localhost>
+References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+/close
+
+On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
+<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote:
+>
+>
+>
+> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
+>
+> ---
+> hey guys everyone knows adventure time sucks!
+>
+> ---
+> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
+>
+> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
+>
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
deleted file mode 100644
index b509cedbe80..00000000000
--- a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { file } from '../../helpers';
-
-describe('Multi-file editor commit sidebar list collapsed', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(listCollapsed);
-
- vm = createComponentWithStore(Component, store);
-
- vm.$store.state.openFiles.push(file('file1'), file('file2'));
- vm.$store.state.openFiles[0].tempFile = true;
- vm.$store.state.openFiles.forEach((f) => {
- Object.assign(f, {
- changed: true,
- });
- });
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders added & modified files count', () => {
- expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1');
- });
-});
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
deleted file mode 100644
index 6f1a1d874d3..00000000000
--- a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import Vue from 'vue';
-import listItem from '~/ide/components/commit_sidebar/list_item.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { file } from '../../helpers';
-
-describe('Multi-file editor commit sidebar list item', () => {
- let vm;
- let f;
-
- beforeEach(() => {
- const Component = Vue.extend(listItem);
-
- f = file('test-file');
-
- vm = mountComponent(Component, {
- file: f,
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders file path', () => {
- expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
- });
-
- describe('computed', () => {
- describe('iconName', () => {
- it('returns modified when not a tempFile', () => {
- expect(vm.iconName).toBe('file-modified');
- });
-
- it('returns addition when not a tempFile', () => {
- f.tempFile = true;
-
- expect(vm.iconName).toBe('file-addition');
- });
- });
-
- describe('iconClass', () => {
- it('returns modified when not a tempFile', () => {
- expect(vm.iconClass).toContain('multi-file-modified');
- });
-
- it('returns addition when not a tempFile', () => {
- f.tempFile = true;
-
- expect(vm.iconClass).toContain('multi-file-addition');
- });
- });
- });
-});
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
deleted file mode 100644
index aeb9de9ace4..00000000000
--- a/spec/javascripts/repo/components/commit_sidebar/list_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import commitSidebarList from '~/ide/components/commit_sidebar/list.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { file } from '../../helpers';
-
-describe('Multi-file editor commit sidebar list', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(commitSidebarList);
-
- vm = createComponentWithStore(Component, store, {
- title: 'Staged',
- fileList: [],
- });
-
- vm.$store.state.rightPanelCollapsed = false;
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('empty file list', () => {
- it('renders no changes text', () => {
- expect(vm.$el.querySelector('.help-block').textContent.trim()).toBe('No changes');
- });
- });
-
- describe('with a list of files', () => {
- beforeEach((done) => {
- const f = file('file name');
- f.changed = true;
- vm.fileList.push(f);
-
- Vue.nextTick(done);
- });
-
- it('renders list', () => {
- expect(vm.$el.querySelectorAll('li').length).toBe(1);
- });
- });
-
- describe('collapsed', () => {
- beforeEach((done) => {
- vm.$store.state.rightPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('hides list', () => {
- expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
- expect(vm.$el.querySelector('.help-block')).toBeNull();
- });
- });
-});
diff --git a/spec/javascripts/repo/components/ide_context_bar_spec.js b/spec/javascripts/repo/components/ide_context_bar_spec.js
deleted file mode 100644
index 935da259a99..00000000000
--- a/spec/javascripts/repo/components/ide_context_bar_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import ideContextBar from '~/ide/components/ide_context_bar.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-
-describe('Multi-file editor right context bar', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(ideContextBar);
-
- vm = createComponentWithStore(Component, store);
-
- vm.$store.state.rightPanelCollapsed = false;
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('collapsed', () => {
- beforeEach((done) => {
- vm.$store.state.rightPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('adds collapsed class', () => {
- expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
- });
-
- it('shows correct icon', () => {
- expect(vm.currentIcon).toBe('angle-double-left');
- });
- });
-
- it('clicking toggle collapse button collapses the bar', () => {
- spyOn(vm, 'setPanelCollapsedStatus').and.returnValue(Promise.resolve());
-
- vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
-
- expect(vm.setPanelCollapsedStatus).toHaveBeenCalledWith({
- side: 'right',
- collapsed: true,
- });
- });
-});
diff --git a/spec/javascripts/repo/components/ide_repo_tree_spec.js b/spec/javascripts/repo/components/ide_repo_tree_spec.js
deleted file mode 100644
index e3bbda514da..00000000000
--- a/spec/javascripts/repo/components/ide_repo_tree_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
-import { file, resetStore } from '../helpers';
-
-describe('IdeRepoTree', () => {
- let vm;
-
- beforeEach(() => {
- const IdeRepoTree = Vue.extend(ideRepoTree);
-
- vm = new IdeRepoTree({
- store,
- propsData: {
- treeId: 'abcproject/mybranch',
- },
- });
-
- vm.$store.state.currentBranch = 'master';
- vm.$store.state.isRoot = true;
- vm.$store.state.trees['abcproject/mybranch'] = {
- tree: [file()],
- };
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders a sidebar', () => {
- const tbody = vm.$el.querySelector('tbody');
-
- expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy();
- expect(tbody.querySelector('.repo-file-options')).toBeFalsy();
- expect(tbody.querySelector('.prev-directory')).toBeFalsy();
- expect(tbody.querySelector('.loading-file')).toBeFalsy();
- expect(tbody.querySelector('.file')).toBeTruthy();
- });
-
- it('renders 3 loading files if tree is loading', (done) => {
- vm.treeId = '123';
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toEqual(3);
-
- done();
- });
- });
-
- it('renders a prev directory if is not root', (done) => {
- vm.$store.state.isRoot = false;
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/repo/components/ide_side_bar_spec.js b/spec/javascripts/repo/components/ide_side_bar_spec.js
deleted file mode 100644
index 79c3c8128e8..00000000000
--- a/spec/javascripts/repo/components/ide_side_bar_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import ideSidebar from '~/ide/components/ide_side_bar.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { resetStore } from '../helpers';
-
-describe('IdeSidebar', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(ideSidebar);
-
- vm = createComponentWithStore(Component, store).$mount();
-
- vm.$store.state.leftPanelCollapsed = false;
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders a sidebar', () => {
- expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
- });
-
- describe('collapsed', () => {
- beforeEach((done) => {
- vm.$store.state.leftPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('adds collapsed class', () => {
- expect(vm.$el.classList).toContain('is-collapsed');
- });
-
- it('shows correct icon', () => {
- expect(vm.currentIcon).toBe('angle-double-right');
- });
- });
-});
diff --git a/spec/javascripts/repo/components/ide_spec.js b/spec/javascripts/repo/components/ide_spec.js
deleted file mode 100644
index 18135177b5e..00000000000
--- a/spec/javascripts/repo/components/ide_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import ide from '~/ide/components/ide.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { file, resetStore } from '../helpers';
-
-describe('ide component', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(ide);
-
- vm = createComponentWithStore(Component, store, {
- emptyStateSvgPath: 'svg',
- }).$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('does not render panel right when no files open', () => {
- expect(vm.$el.querySelector('.panel-right')).toBeNull();
- });
-
- it('renders panel right when files are open', (done) => {
- vm.$store.state.trees['abcproject/mybranch'] = {
- tree: [file()],
- };
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.panel-right')).toBeNull();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/repo/components/new_branch_form_spec.js b/spec/javascripts/repo/components/new_branch_form_spec.js
deleted file mode 100644
index 82597fc75e8..00000000000
--- a/spec/javascripts/repo/components/new_branch_form_spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import newBranchForm from '~/ide/components/new_branch_form.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { resetStore } from '../helpers';
-
-describe('Multi-file editor new branch form', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(newBranchForm);
-
- vm = createComponentWithStore(Component, store);
-
- vm.$store.state.currentBranch = 'master';
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- describe('template', () => {
- it('renders submit as disabled', () => {
- expect(vm.$el.querySelector('.btn').getAttribute('disabled')).toBe('disabled');
- });
-
- it('enables the submit button when branch is not empty', (done) => {
- vm.branchName = 'testing';
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn').getAttribute('disabled')).toBeNull();
-
- done();
- });
- });
-
- it('displays current branch creating from', (done) => {
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('p').textContent.replace(/\s+/g, ' ').trim()).toBe('Create from: master');
-
- done();
- });
- });
- });
-
- describe('submitNewBranch', () => {
- beforeEach(() => {
- spyOn(vm, 'createNewBranch').and.returnValue(Promise.resolve());
- });
-
- it('sets to loading', () => {
- vm.submitNewBranch();
-
- expect(vm.loading).toBeTruthy();
- });
-
- it('hides current flash element', (done) => {
- vm.$refs.flashContainer.innerHTML = '<div class="flash-alert"></div>';
-
- vm.submitNewBranch();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.flash-alert')).toBeNull();
-
- done();
- });
- });
-
- it('calls createdNewBranch with branchName', () => {
- vm.branchName = 'testing';
-
- vm.submitNewBranch();
-
- expect(vm.createNewBranch).toHaveBeenCalledWith('testing');
- });
- });
-
- describe('submitNewBranch with error', () => {
- beforeEach(() => {
- spyOn(vm, 'createNewBranch').and.returnValue(Promise.reject({
- json: () => Promise.resolve({
- message: 'error message',
- }),
- }));
- });
-
- it('sets loading to false', (done) => {
- vm.loading = true;
-
- vm.submitNewBranch();
-
- setTimeout(() => {
- expect(vm.loading).toBeFalsy();
-
- done();
- });
- });
-
- it('creates flash element', (done) => {
- vm.submitNewBranch();
-
- setTimeout(() => {
- expect(vm.$el.querySelector('.flash-alert')).not.toBeNull();
- expect(vm.$el.querySelector('.flash-alert').textContent.trim()).toBe('error message');
-
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
deleted file mode 100644
index 4a8e4445e2f..00000000000
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import newDropdown from '~/ide/components/new_dropdown/index.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { resetStore } from '../../helpers';
-
-describe('new dropdown component', () => {
- let vm;
-
- beforeEach(() => {
- const component = Vue.extend(newDropdown);
-
- vm = createComponentWithStore(component, store, {
- branch: 'master',
- path: '',
- });
-
- vm.$store.state.currentProjectId = 'abcproject';
- vm.$store.state.path = '';
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders new file, upload and new directory links', () => {
- expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file');
- expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('Upload file');
- expect(vm.$el.querySelectorAll('a')[2].textContent.trim()).toBe('New directory');
- });
-
- describe('createNewItem', () => {
- it('sets modalType to blob when new file is clicked', () => {
- vm.$el.querySelectorAll('a')[0].click();
-
- expect(vm.modalType).toBe('blob');
- });
-
- it('sets modalType to tree when new directory is clicked', () => {
- vm.$el.querySelectorAll('a')[2].click();
-
- expect(vm.modalType).toBe('tree');
- });
-
- it('opens modal when link is clicked', (done) => {
- vm.$el.querySelectorAll('a')[0].click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
-
- done();
- });
- });
- });
-
- describe('hideModal', () => {
- beforeAll((done) => {
- vm.openModal = true;
- Vue.nextTick(done);
- });
-
- it('closes modal after toggling', (done) => {
- vm.hideModal();
-
- Vue.nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.modal')).toBeNull();
- })
- .then(done)
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
deleted file mode 100644
index d6a1fdd115c..00000000000
--- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js
+++ /dev/null
@@ -1,237 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import modal from '~/ide/components/new_dropdown/modal.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { file, resetStore } from '../../helpers';
-
-describe('new file modal component', () => {
- const Component = Vue.extend(modal);
- let vm;
- let projectTree;
-
- beforeEach(() => {
- spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({
- data: {
- id: '123',
- },
- }));
-
- spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
- data: {
- commit: {
- id: '123branch',
- },
- },
- }));
-
- spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
- headers: {
- 'page-title': 'test',
- },
- json: () => Promise.resolve({
- last_commit_path: 'last_commit_path',
- parent_tree_url: 'parent_tree_url',
- path: '/',
- trees: [{ name: 'tree' }],
- blobs: [{ name: 'blob' }],
- submodules: [{ name: 'submodule' }],
- }),
- }));
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- ['tree', 'blob'].forEach((type) => {
- describe(type, () => {
- beforeEach(() => {
- store.state.projects.abcproject = {
- web_url: '',
- };
- store.state.trees = [];
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
- projectTree = store.state.trees['abcproject/mybranch'];
- store.state.currentProjectId = 'abcproject';
-
- vm = createComponentWithStore(Component, store, {
- type,
- branchId: 'master',
- path: '',
- parent: projectTree,
- });
-
- vm.entryName = 'testing';
-
- vm.$mount();
- });
-
- it(`sets modal title as ${type}`, () => {
- const title = type === 'tree' ? 'directory' : 'file';
-
- expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Create new ${title}`);
- });
-
- it(`sets button label as ${type}`, () => {
- const title = type === 'tree' ? 'directory' : 'file';
-
- expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Create ${title}`);
- });
-
- it(`sets form label as ${type}`, () => {
- const title = type === 'tree' ? 'Directory' : 'File';
-
- expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe(`${title} name`);
- });
-
- describe('createEntryInStore', () => {
- it('calls createTempEntry', () => {
- spyOn(vm, 'createTempEntry');
-
- vm.createEntryInStore();
-
- expect(vm.createTempEntry).toHaveBeenCalledWith({
- projectId: 'abcproject',
- branchId: 'master',
- parent: projectTree,
- name: 'testing',
- type,
- });
- });
-
- it('sets editMode to true', (done) => {
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(vm.$store.state.editMode).toBeTruthy();
-
- done();
- });
- });
-
- it('toggles blob view', (done) => {
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(vm.$store.state.currentBlobView).toBe('repo-editor');
-
- done();
- });
- });
-
- it('opens newly created file', (done) => {
- if (type === 'blob') {
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(vm.$store.state.openFiles.length).toBe(1);
- expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep');
-
- done();
- });
- } else {
- done();
- }
- });
-
- if (type === 'blob') {
- it('creates new file', (done) => {
- vm.createEntryInStore();
-
- setTimeout(() => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('testing');
- expect(baseTree[0].type).toBe('blob');
- expect(baseTree[0].tempFile).toBeTruthy();
-
- done();
- });
- });
-
- it('does not create temp file when file already exists', (done) => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- baseTree.push(file('testing', '1', type));
-
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('testing');
- expect(baseTree[0].type).toBe('blob');
- expect(baseTree[0].tempFile).toBeFalsy();
-
- done();
- });
- });
- } else {
- it('creates new tree', () => {
- vm.createEntryInStore();
-
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('testing');
- expect(baseTree[0].type).toBe('tree');
- expect(baseTree[0].tempFile).toBeTruthy();
- });
-
- it('creates multiple trees when entryName has slashes', () => {
- vm.entryName = 'app/test';
- vm.createEntryInStore();
-
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('app');
- });
-
- it('creates tree in existing tree', () => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- baseTree.push(file('app', '1', 'tree'));
-
- vm.entryName = 'app/test';
- vm.createEntryInStore();
-
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('app');
- expect(baseTree[0].tempFile).toBeFalsy();
- expect(baseTree[0].tree[0].tempFile).toBeTruthy();
- expect(baseTree[0].tree[0].name).toBe('test');
- });
-
- it('does not create new tree when already exists', () => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- baseTree.push(file('app', '1', 'tree'));
-
- vm.entryName = 'app';
- vm.createEntryInStore();
-
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe('app');
- expect(baseTree[0].tempFile).toBeFalsy();
- expect(baseTree[0].tree.length).toBe(0);
- });
- }
- });
- });
- });
-
- it('focuses field on mount', () => {
- document.body.innerHTML += '<div class="js-test"></div>';
-
- vm = createComponentWithStore(Component, store, {
- type: 'tree',
- projectId: 'abcproject',
- branchId: 'master',
- path: '',
- }).$mount('.js-test');
-
- expect(document.activeElement).toBe(vm.$refs.fieldName);
-
- vm.$el.remove();
- });
-});
diff --git a/spec/javascripts/repo/components/new_dropdown/upload_spec.js b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
deleted file mode 100644
index ee8aab3a252..00000000000
--- a/spec/javascripts/repo/components/new_dropdown/upload_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import Vue from 'vue';
-import upload from '~/ide/components/new_dropdown/upload.vue';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import { resetStore } from '../../helpers';
-
-describe('new dropdown upload', () => {
- let vm;
- let projectTree;
-
- beforeEach(() => {
- spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({
- data: {
- id: '123',
- },
- }));
-
- spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
- data: {
- commit: {
- id: '123branch',
- },
- },
- }));
-
- spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
- headers: {
- 'page-title': 'test',
- },
- json: () => Promise.resolve({
- last_commit_path: 'last_commit_path',
- parent_tree_url: 'parent_tree_url',
- path: '/',
- trees: [{ name: 'tree' }],
- blobs: [{ name: 'blob' }],
- submodules: [{ name: 'submodule' }],
- }),
- }));
-
- const Component = Vue.extend(upload);
-
- store.state.projects.abcproject = {
- web_url: '',
- };
- store.state.currentProjectId = 'abcproject';
- store.state.trees = [];
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
- projectTree = store.state.trees['abcproject/mybranch'];
-
- vm = createComponentWithStore(Component, store, {
- branchId: 'master',
- path: '',
- parent: projectTree,
- });
-
- vm.entryName = 'testing';
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- describe('readFile', () => {
- beforeEach(() => {
- spyOn(FileReader.prototype, 'readAsText');
- spyOn(FileReader.prototype, 'readAsDataURL');
- });
-
- it('calls readAsText for text files', () => {
- const file = {
- type: 'text/html',
- };
-
- vm.readFile(file);
-
- expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(file);
- });
-
- it('calls readAsDataURL for non-text files', () => {
- const file = {
- type: 'images/png',
- };
-
- vm.readFile(file);
-
- expect(FileReader.prototype.readAsDataURL).toHaveBeenCalledWith(file);
- });
- });
-
- describe('createFile', () => {
- const target = {
- result: 'content',
- };
- const binaryTarget = {
- result: 'base64,base64content',
- };
- const file = {
- name: 'file',
- };
-
- it('creates new file', (done) => {
- vm.createFile(target, file, true);
-
- vm.$nextTick(() => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe(file.name);
- expect(baseTree[0].content).toBe(target.result);
-
- done();
- });
- });
-
- it('creates new file in path', (done) => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- const tree = {
- type: 'tree',
- name: 'testing',
- path: 'testing',
- tree: [],
- };
- baseTree.push(tree);
-
- vm.parent = tree;
- vm.createFile(target, file, true);
-
- vm.$nextTick(() => {
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].tree[0].name).toBe(file.name);
- expect(baseTree[0].tree[0].content).toBe(target.result);
- expect(baseTree[0].tree[0].path).toBe(`testing/${file.name}`);
-
- done();
- });
- });
-
- it('splits content on base64 if binary', (done) => {
- vm.createFile(binaryTarget, file, false);
-
- vm.$nextTick(() => {
- const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].name).toBe(file.name);
- expect(baseTree[0].content).toBe(binaryTarget.result.split('base64,')[1]);
- expect(baseTree[0].base64).toBe(true);
-
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js
deleted file mode 100644
index 934ada9dec2..00000000000
--- a/spec/javascripts/repo/components/repo_commit_section_spec.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import repoCommitSection from '~/ide/components/repo_commit_section.vue';
-import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
-import { file, resetStore } from '../helpers';
-
-describe('RepoCommitSection', () => {
- let vm;
-
- function createComponent() {
- const RepoCommitSection = Vue.extend(repoCommitSection);
-
- const comp = new RepoCommitSection({
- store,
- }).$mount();
-
- comp.$store.state.currentProjectId = 'abcproject';
- comp.$store.state.currentBranchId = 'master';
- comp.$store.state.projects.abcproject = {
- web_url: '',
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
-
- comp.$store.state.rightPanelCollapsed = false;
- comp.$store.state.currentBranch = 'master';
- comp.$store.state.openFiles = [file('file1'), file('file2')];
- comp.$store.state.openFiles.forEach(f => Object.assign(f, {
- changed: true,
- content: 'testing',
- }));
-
- return comp.$mount();
- }
-
- beforeEach((done) => {
- vm = createComponent();
-
- spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
- headers: {
- 'page-title': 'test',
- },
- json: () => Promise.resolve({
- last_commit_path: 'last_commit_path',
- parent_tree_url: 'parent_tree_url',
- path: '/',
- trees: [{ name: 'tree' }],
- blobs: [{ name: 'blob' }],
- submodules: [{ name: 'submodule' }],
- }),
- }));
-
- Vue.nextTick(done);
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders a commit section', () => {
- const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
- const submitCommit = vm.$el.querySelector('form .btn');
-
- expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
- expect(changedFileElements.length).toEqual(2);
-
- changedFileElements.forEach((changedFile, i) => {
- expect(changedFile.textContent.trim()).toEqual(vm.$store.getters.changedFiles[i].path);
- });
-
- expect(submitCommit.disabled).toBeTruthy();
- expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
- });
-
- describe('when submitting', () => {
- let changedFiles;
-
- beforeEach(() => {
- vm.commitMessage = 'testing';
- changedFiles = JSON.parse(JSON.stringify(vm.$store.getters.changedFiles));
-
- spyOn(service, 'commit').and.returnValue(Promise.resolve({
- data: {
- short_id: '1',
- stats: {},
- },
- }));
- });
-
- it('allows you to submit', () => {
- expect(vm.$el.querySelector('form .btn').disabled).toBeTruthy();
- });
-
- it('submits commit', (done) => {
- vm.makeCommit();
-
- // Wait for the branch check to finish
- getSetTimeoutPromise()
- .then(() => Vue.nextTick())
- .then(() => {
- const args = service.commit.calls.allArgs()[0];
- const { commit_message, actions, branch: payloadBranch } = args[1];
-
- expect(commit_message).toBe('testing');
- expect(actions.length).toEqual(2);
- expect(payloadBranch).toEqual('master');
- expect(actions[0].action).toEqual('update');
- expect(actions[1].action).toEqual('update');
- expect(actions[0].content).toEqual(changedFiles[0].content);
- expect(actions[1].content).toEqual(changedFiles[1].content);
- expect(actions[0].file_path).toEqual(changedFiles[0].path);
- expect(actions[1].file_path).toEqual(changedFiles[1].path);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('redirects to MR creation page if start new MR checkbox checked', (done) => {
- spyOn(urlUtils, 'visitUrl');
- vm.startNewMR = true;
-
- vm.makeCommit();
-
- getSetTimeoutPromise()
- .then(() => Vue.nextTick())
- .then(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js
deleted file mode 100644
index 2895b794506..00000000000
--- a/spec/javascripts/repo/components/repo_edit_button_spec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoEditButton from '~/ide/components/repo_edit_button.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoEditButton', () => {
- let vm;
-
- beforeEach(() => {
- const f = file();
- const RepoEditButton = Vue.extend(repoEditButton);
-
- vm = new RepoEditButton({
- store,
- });
-
- f.active = true;
- vm.$store.dispatch('setInitialData', {
- canCommit: true,
- onTopOfBranch: true,
- });
- vm.$store.state.openFiles.push(f);
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders an edit button', () => {
- vm.$mount();
-
- expect(vm.$el.querySelector('.btn')).not.toBeNull();
- expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit');
- });
-
- it('renders edit button with cancel text', () => {
- vm.$store.state.editMode = true;
-
- vm.$mount();
-
- expect(vm.$el.querySelector('.btn')).not.toBeNull();
- expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit');
- });
-
- it('toggles edit mode on click', (done) => {
- vm.$mount();
-
- vm.$el.querySelector('.btn').click();
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit');
-
- done();
- });
- });
-
- describe('discardPopupOpen', () => {
- beforeEach(() => {
- vm.$store.state.discardPopupOpen = true;
- vm.$store.state.editMode = true;
- vm.$store.state.openFiles[0].changed = true;
-
- vm.$mount();
- });
-
- it('renders popup', () => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
- });
-
- it('removes all changed files', (done) => {
- vm.$el.querySelector('.btn-warning').click();
-
- vm.$nextTick(() => {
- expect(vm.$store.getters.changedFiles.length).toBe(0);
- expect(vm.$el.querySelector('.modal')).toBeNull();
-
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js
deleted file mode 100644
index e7b2ed08acd..00000000000
--- a/spec/javascripts/repo/components/repo_editor_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoEditor from '~/ide/components/repo_editor.vue';
-import monacoLoader from '~/ide/monaco_loader';
-import { file, resetStore } from '../helpers';
-
-describe('RepoEditor', () => {
- let vm;
-
- beforeEach((done) => {
- const f = file();
- const RepoEditor = Vue.extend(repoEditor);
-
- vm = new RepoEditor({
- store,
- });
-
- f.active = true;
- f.tempFile = true;
- vm.$store.state.openFiles.push(f);
- vm.$store.getters.activeFile.html = 'testing';
- vm.monaco = true;
-
- vm.$mount();
-
- monacoLoader(['vs/editor/editor.main'], () => {
- setTimeout(done, 0);
- });
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders an ide container', (done) => {
- Vue.nextTick(() => {
- expect(vm.shouldHideEditor).toBeFalsy();
-
- done();
- });
- });
-
- describe('when open file is binary and not raw', () => {
- beforeEach((done) => {
- vm.$store.getters.activeFile.binary = true;
-
- Vue.nextTick(done);
- });
-
- it('does not render the IDE', () => {
- expect(vm.shouldHideEditor).toBeTruthy();
- });
-
- it('shows activeFile html', () => {
- expect(vm.$el.textContent).toContain('testing');
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_file_buttons_spec.js b/spec/javascripts/repo/components/repo_file_buttons_spec.js
deleted file mode 100644
index 115569a9117..00000000000
--- a/spec/javascripts/repo/components/repo_file_buttons_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoFileButtons from '~/ide/components/repo_file_buttons.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoFileButtons', () => {
- const activeFile = file();
- let vm;
-
- function createComponent() {
- const RepoFileButtons = Vue.extend(repoFileButtons);
-
- activeFile.rawPath = 'test';
- activeFile.blamePath = 'test';
- activeFile.commitsPath = 'test';
- activeFile.active = true;
- store.state.openFiles.push(activeFile);
-
- return new RepoFileButtons({
- store,
- }).$mount();
- }
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders Raw, Blame, History, Permalink and Preview toggle', (done) => {
- vm = createComponent();
-
- vm.$nextTick(() => {
- const raw = vm.$el.querySelector('.raw');
- const blame = vm.$el.querySelector('.blame');
- const history = vm.$el.querySelector('.history');
-
- expect(raw.href).toMatch(`/${activeFile.rawPath}`);
- expect(raw.textContent.trim()).toEqual('Raw');
- expect(blame.href).toMatch(`/${activeFile.blamePath}`);
- expect(blame.textContent.trim()).toEqual('Blame');
- expect(history.href).toMatch(`/${activeFile.commitsPath}`);
- expect(history.textContent.trim()).toEqual('History');
- expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual('Permalink');
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js
deleted file mode 100644
index 27b55ed1f87..00000000000
--- a/spec/javascripts/repo/components/repo_file_spec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoFile from '~/ide/components/repo_file.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoFile', () => {
- const updated = 'updated';
- let vm;
-
- function createComponent(propsData) {
- const RepoFile = Vue.extend(repoFile);
-
- return new RepoFile({
- store,
- propsData,
- }).$mount();
- }
-
- afterEach(() => {
- resetStore(vm.$store);
- });
-
- it('renders link, icon and name', () => {
- const RepoFile = Vue.extend(repoFile);
- vm = new RepoFile({
- store,
- propsData: {
- file: file('t4'),
- },
- });
- spyOn(vm, 'timeFormated').and.returnValue(updated);
- vm.$mount();
-
- const name = vm.$el.querySelector('.repo-file-name');
-
- expect(name.href).toMatch('');
- expect(name.textContent.trim()).toEqual(vm.file.name);
- });
-
- it('does render if hasFiles is true and is loading tree', () => {
- vm = createComponent({
- file: file('t1'),
- });
-
- expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy();
- });
-
- it('does not render commit message and datetime if mini', (done) => {
- vm = createComponent({
- file: file('t2'),
- });
- vm.$store.state.openFiles.push(vm.file);
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.commit-message')).toBeFalsy();
- expect(vm.$el.querySelector('.commit-update')).toBeFalsy();
-
- done();
- });
- });
-
- it('fires clickFile when the link is clicked', () => {
- vm = createComponent({
- file: file('t3'),
- });
-
- spyOn(vm, 'clickFile');
-
- vm.$el.click();
-
- expect(vm.clickFile).toHaveBeenCalledWith(vm.file);
- });
-
- describe('submodule', () => {
- let f;
-
- beforeEach(() => {
- f = file('submodule name', '123456789');
- f.type = 'submodule';
-
- vm = createComponent({
- file: f,
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders submodule short ID', () => {
- expect(vm.$el.querySelector('.commit-sha').textContent.trim()).toBe('12345678');
- });
-
- it('renders ID next to submodule name', () => {
- expect(vm.$el.querySelector('td').textContent.replace(/\s+/g, ' ')).toContain('submodule name @ 12345678');
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_loading_file_spec.js b/spec/javascripts/repo/components/repo_loading_file_spec.js
deleted file mode 100644
index 18366fb89bc..00000000000
--- a/spec/javascripts/repo/components/repo_loading_file_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
-import { resetStore } from '../helpers';
-
-describe('RepoLoadingFile', () => {
- let vm;
-
- function createComponent() {
- const RepoLoadingFile = Vue.extend(repoLoadingFile);
-
- return new RepoLoadingFile({
- store,
- }).$mount();
- }
-
- function assertLines(lines) {
- lines.forEach((line, n) => {
- const index = n + 1;
- expect(line.classList.contains(`skeleton-line-${index}`)).toBeTruthy();
- });
- }
-
- function assertColumns(columns) {
- columns.forEach((column) => {
- const container = column.querySelector('.animation-container');
- const lines = [...container.querySelectorAll(':scope > div')];
-
- expect(container).toBeTruthy();
- expect(lines.length).toEqual(6);
- assertLines(lines);
- });
- }
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders 3 columns of animated LoC', () => {
- vm = createComponent();
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(3);
- assertColumns(columns);
- });
-
- it('renders 1 column of animated LoC if isMini', (done) => {
- vm = createComponent();
- vm.$store.state.leftPanelCollapsed = true;
- vm.$store.state.openFiles.push('test');
-
- vm.$nextTick(() => {
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(1);
- assertColumns(columns);
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_prev_directory_spec.js b/spec/javascripts/repo/components/repo_prev_directory_spec.js
deleted file mode 100644
index ff26cab2262..00000000000
--- a/spec/javascripts/repo/components/repo_prev_directory_spec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoPrevDirectory from '~/ide/components/repo_prev_directory.vue';
-import { resetStore } from '../helpers';
-
-describe('RepoPrevDirectory', () => {
- let vm;
- const parentLink = 'parent';
- function createComponent() {
- const RepoPrevDirectory = Vue.extend(repoPrevDirectory);
-
- const comp = new RepoPrevDirectory({
- store,
- });
-
- comp.$store.state.parentTreeUrl = parentLink;
-
- return comp.$mount();
- }
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders a prev dir link', () => {
- const link = vm.$el.querySelector('a');
-
- expect(link.href).toMatch(`/${parentLink}`);
- expect(link.textContent).toEqual('...');
- });
-
- it('clicking row triggers getTreeData', () => {
- spyOn(vm, 'getTreeData');
-
- vm.$el.querySelector('td').click();
-
- expect(vm.getTreeData).toHaveBeenCalledWith({ endpoint: parentLink });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_preview_spec.js b/spec/javascripts/repo/components/repo_preview_spec.js
deleted file mode 100644
index e90837e4cb2..00000000000
--- a/spec/javascripts/repo/components/repo_preview_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoPreview from '~/ide/components/repo_preview.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoPreview', () => {
- let vm;
-
- function createComponent() {
- const f = file();
- const RepoPreview = Vue.extend(repoPreview);
-
- const comp = new RepoPreview({
- store,
- });
-
- f.active = true;
- f.html = 'test';
-
- comp.$store.state.openFiles.push(f);
-
- return comp.$mount();
- }
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders a div with the activeFile html', () => {
- vm = createComponent();
-
- expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.innerHTML).toContain('test');
- });
-});
diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js
deleted file mode 100644
index 933e8d3a06a..00000000000
--- a/spec/javascripts/repo/components/repo_tab_spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoTab from '~/ide/components/repo_tab.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoTab', () => {
- let vm;
-
- function createComponent(propsData) {
- const RepoTab = Vue.extend(repoTab);
-
- return new RepoTab({
- store,
- propsData,
- }).$mount();
- }
-
- afterEach(() => {
- resetStore(vm.$store);
- });
-
- it('renders a close link and a name link', () => {
- vm = createComponent({
- tab: file(),
- });
- vm.$store.state.openFiles.push(vm.tab);
- const close = vm.$el.querySelector('.multi-file-tab-close');
- const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`);
-
- expect(close.querySelector('.fa-times')).toBeTruthy();
- expect(name.textContent.trim()).toEqual(vm.tab.name);
- });
-
- it('fires clickFile when the link is clicked', () => {
- vm = createComponent({
- tab: file(),
- });
-
- spyOn(vm, 'clickFile');
-
- vm.$el.click();
-
- expect(vm.clickFile).toHaveBeenCalledWith(vm.tab);
- });
-
- it('calls closeFile when clicking close button', () => {
- vm = createComponent({
- tab: file(),
- });
-
- spyOn(vm, 'closeFile');
-
- vm.$el.querySelector('.multi-file-tab-close').click();
-
- expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab });
- });
-
- it('renders an fa-circle icon if tab is changed', () => {
- const tab = file('changedFile');
- tab.changed = true;
- vm = createComponent({
- tab,
- });
-
- expect(vm.$el.querySelector('.multi-file-tab-close .fa-circle')).not.toBeNull();
- });
-
- describe('methods', () => {
- describe('closeTab', () => {
- it('does not close tab if is changed', (done) => {
- const tab = file('closeFile');
- tab.changed = true;
- tab.opened = true;
- vm = createComponent({
- tab,
- });
- vm.$store.state.openFiles.push(tab);
- vm.$store.dispatch('setFileActive', tab);
-
- vm.$el.querySelector('.multi-file-tab-close').click();
-
- vm.$nextTick(() => {
- expect(tab.opened).toBeTruthy();
-
- done();
- });
- });
-
- it('closes tab when clicking close btn', (done) => {
- const tab = file('lose');
- tab.opened = true;
- vm = createComponent({
- tab,
- });
- vm.$store.state.openFiles.push(tab);
- vm.$store.dispatch('setFileActive', tab);
-
- vm.$el.querySelector('.multi-file-tab-close').click();
-
- vm.$nextTick(() => {
- expect(tab.opened).toBeFalsy();
-
- done();
- });
- });
- });
- });
-});
diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js
deleted file mode 100644
index 2c363364d70..00000000000
--- a/spec/javascripts/repo/components/repo_tabs_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoTabs from '~/ide/components/repo_tabs.vue';
-import { file, resetStore } from '../helpers';
-
-describe('RepoTabs', () => {
- const openedFiles = [file('open1'), file('open2')];
- let vm;
-
- function createComponent() {
- const RepoTabs = Vue.extend(repoTabs);
-
- return new RepoTabs({
- store,
- }).$mount();
- }
-
- afterEach(() => {
- resetStore(vm.$store);
- });
-
- it('renders a list of tabs', (done) => {
- vm = createComponent();
- openedFiles[0].active = true;
- vm.$store.state.openFiles = openedFiles;
-
- vm.$nextTick(() => {
- const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
-
- expect(tabs.length).toEqual(2);
- expect(tabs[0].classList.contains('active')).toBeTruthy();
- expect(tabs[1].classList.contains('active')).toBeFalsy();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/repo/helpers.js b/spec/javascripts/repo/helpers.js
deleted file mode 100644
index ac43d221198..00000000000
--- a/spec/javascripts/repo/helpers.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { decorateData } from '~/ide/stores/utils';
-import state from '~/ide/stores/state';
-
-export const resetStore = (store) => {
- store.replaceState(state());
-};
-
-export const file = (name = 'name', id = name, type = '') => decorateData({
- id,
- type,
- icon: 'icon',
- url: 'url',
- name,
- path: name,
- lastCommit: {},
-});
diff --git a/spec/javascripts/repo/lib/common/disposable_spec.js b/spec/javascripts/repo/lib/common/disposable_spec.js
deleted file mode 100644
index af12ca15369..00000000000
--- a/spec/javascripts/repo/lib/common/disposable_spec.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import Disposable from '~/ide/lib/common/disposable';
-
-describe('Multi-file editor library disposable class', () => {
- let instance;
- let disposableClass;
-
- beforeEach(() => {
- instance = new Disposable();
-
- disposableClass = {
- dispose: jasmine.createSpy('dispose'),
- };
- });
-
- afterEach(() => {
- instance.dispose();
- });
-
- describe('add', () => {
- it('adds disposable classes', () => {
- instance.add(disposableClass);
-
- expect(instance.disposers.size).toBe(1);
- });
- });
-
- describe('dispose', () => {
- beforeEach(() => {
- instance.add(disposableClass);
- });
-
- it('calls dispose on all cached disposers', () => {
- instance.dispose();
-
- expect(disposableClass.dispose).toHaveBeenCalled();
- });
-
- it('clears cached disposers', () => {
- instance.dispose();
-
- expect(instance.disposers.size).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/common/model_manager_spec.js b/spec/javascripts/repo/lib/common/model_manager_spec.js
deleted file mode 100644
index 563c2e33834..00000000000
--- a/spec/javascripts/repo/lib/common/model_manager_spec.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import ModelManager from '~/ide/lib/common/model_manager';
-import { file } from '../../helpers';
-
-describe('Multi-file editor library model manager', () => {
- let instance;
-
- beforeEach((done) => {
- monacoLoader(['vs/editor/editor.main'], () => {
- instance = new ModelManager(monaco);
-
- done();
- });
- });
-
- afterEach(() => {
- instance.dispose();
- });
-
- describe('addModel', () => {
- it('caches model', () => {
- instance.addModel(file());
-
- expect(instance.models.size).toBe(1);
- });
-
- it('caches model by file path', () => {
- instance.addModel(file('path-name'));
-
- expect(instance.models.keys().next().value).toBe('path-name');
- });
-
- it('adds model into disposable', () => {
- spyOn(instance.disposable, 'add').and.callThrough();
-
- instance.addModel(file());
-
- expect(instance.disposable.add).toHaveBeenCalled();
- });
-
- it('returns cached model', () => {
- spyOn(instance.models, 'get').and.callThrough();
-
- instance.addModel(file());
- instance.addModel(file());
-
- expect(instance.models.get).toHaveBeenCalled();
- });
- });
-
- describe('hasCachedModel', () => {
- it('returns false when no models exist', () => {
- expect(instance.hasCachedModel('path')).toBeFalsy();
- });
-
- it('returns true when model exists', () => {
- instance.addModel(file('path-name'));
-
- expect(instance.hasCachedModel('path-name')).toBeTruthy();
- });
- });
-
- describe('dispose', () => {
- it('clears cached models', () => {
- instance.addModel(file());
-
- instance.dispose();
-
- expect(instance.models.size).toBe(0);
- });
-
- it('calls disposable dispose', () => {
- spyOn(instance.disposable, 'dispose').and.callThrough();
-
- instance.dispose();
-
- expect(instance.disposable.dispose).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/common/model_spec.js b/spec/javascripts/repo/lib/common/model_spec.js
deleted file mode 100644
index 878a4a3f3fe..00000000000
--- a/spec/javascripts/repo/lib/common/model_spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import Model from '~/ide/lib/common/model';
-import { file } from '../../helpers';
-
-describe('Multi-file editor library model', () => {
- let model;
-
- beforeEach((done) => {
- monacoLoader(['vs/editor/editor.main'], () => {
- model = new Model(monaco, file('path'));
-
- done();
- });
- });
-
- afterEach(() => {
- model.dispose();
- });
-
- it('creates original model & new model', () => {
- expect(model.originalModel).not.toBeNull();
- expect(model.model).not.toBeNull();
- });
-
- describe('path', () => {
- it('returns file path', () => {
- expect(model.path).toBe('path');
- });
- });
-
- describe('getModel', () => {
- it('returns model', () => {
- expect(model.getModel()).toBe(model.model);
- });
- });
-
- describe('getOriginalModel', () => {
- it('returns original model', () => {
- expect(model.getOriginalModel()).toBe(model.originalModel);
- });
- });
-
- describe('onChange', () => {
- it('caches event by path', () => {
- model.onChange(() => {});
-
- expect(model.events.size).toBe(1);
- expect(model.events.keys().next().value).toBe('path');
- });
-
- it('calls callback on change', (done) => {
- const spy = jasmine.createSpy();
- model.onChange(spy);
-
- model.getModel().setValue('123');
-
- setTimeout(() => {
- expect(spy).toHaveBeenCalledWith(model.getModel(), jasmine.anything());
- done();
- });
- });
- });
-
- describe('dispose', () => {
- it('calls disposable dispose', () => {
- spyOn(model.disposable, 'dispose').and.callThrough();
-
- model.dispose();
-
- expect(model.disposable.dispose).toHaveBeenCalled();
- });
-
- it('clears events', () => {
- model.onChange(() => {});
-
- expect(model.events.size).toBe(1);
-
- model.dispose();
-
- expect(model.events.size).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/decorations/controller_spec.js b/spec/javascripts/repo/lib/decorations/controller_spec.js
deleted file mode 100644
index fea12d74dca..00000000000
--- a/spec/javascripts/repo/lib/decorations/controller_spec.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
-import DecorationsController from '~/ide/lib/decorations/controller';
-import Model from '~/ide/lib/common/model';
-import { file } from '../../helpers';
-
-describe('Multi-file editor library decorations controller', () => {
- let editorInstance;
- let controller;
- let model;
-
- beforeEach((done) => {
- monacoLoader(['vs/editor/editor.main'], () => {
- editorInstance = editor.create(monaco);
- editorInstance.createInstance(document.createElement('div'));
-
- controller = new DecorationsController(editorInstance);
- model = new Model(monaco, file('path'));
-
- done();
- });
- });
-
- afterEach(() => {
- model.dispose();
- editorInstance.dispose();
- controller.dispose();
- });
-
- describe('getAllDecorationsForModel', () => {
- it('returns empty array when no decorations exist for model', () => {
- const decorations = controller.getAllDecorationsForModel(model);
-
- expect(decorations).toEqual([]);
- });
-
- it('returns decorations by model URL', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- const decorations = controller.getAllDecorationsForModel(model);
-
- expect(decorations[0]).toEqual({ decoration: 'decorationValue' });
- });
- });
-
- describe('addDecorations', () => {
- it('caches decorations in a new map', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- expect(controller.decorations.size).toBe(1);
- });
-
- it('does not create new cache model', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue2' }]);
-
- expect(controller.decorations.size).toBe(1);
- });
-
- it('caches decorations by model URL', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- expect(controller.decorations.size).toBe(1);
- expect(controller.decorations.keys().next().value).toBe('path');
- });
-
- it('calls decorate method', () => {
- spyOn(controller, 'decorate');
-
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- expect(controller.decorate).toHaveBeenCalled();
- });
- });
-
- describe('decorate', () => {
- it('sets decorations on editor instance', () => {
- spyOn(controller.editor.instance, 'deltaDecorations');
-
- controller.decorate(model);
-
- expect(controller.editor.instance.deltaDecorations).toHaveBeenCalledWith([], []);
- });
-
- it('caches decorations', () => {
- spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]);
-
- controller.decorate(model);
-
- expect(controller.editorDecorations.size).toBe(1);
- });
-
- it('caches decorations by model URL', () => {
- spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]);
-
- controller.decorate(model);
-
- expect(controller.editorDecorations.keys().next().value).toBe('path');
- });
- });
-
- describe('dispose', () => {
- it('clears cached decorations', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- controller.dispose();
-
- expect(controller.decorations.size).toBe(0);
- });
-
- it('clears cached editorDecorations', () => {
- controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]);
-
- controller.dispose();
-
- expect(controller.editorDecorations.size).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/diff/controller_spec.js b/spec/javascripts/repo/lib/diff/controller_spec.js
deleted file mode 100644
index 1d55c165260..00000000000
--- a/spec/javascripts/repo/lib/diff/controller_spec.js
+++ /dev/null
@@ -1,176 +0,0 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
-import ModelManager from '~/ide/lib/common/model_manager';
-import DecorationsController from '~/ide/lib/decorations/controller';
-import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller';
-import { computeDiff } from '~/ide/lib/diff/diff';
-import { file } from '../../helpers';
-
-describe('Multi-file editor library dirty diff controller', () => {
- let editorInstance;
- let controller;
- let modelManager;
- let decorationsController;
- let model;
-
- beforeEach((done) => {
- monacoLoader(['vs/editor/editor.main'], () => {
- editorInstance = editor.create(monaco);
- editorInstance.createInstance(document.createElement('div'));
-
- modelManager = new ModelManager(monaco);
- decorationsController = new DecorationsController(editorInstance);
-
- model = modelManager.addModel(file());
-
- controller = new DirtyDiffController(modelManager, decorationsController);
-
- done();
- });
- });
-
- afterEach(() => {
- controller.dispose();
- model.dispose();
- decorationsController.dispose();
- editorInstance.dispose();
- });
-
- describe('getDiffChangeType', () => {
- ['added', 'removed', 'modified'].forEach((type) => {
- it(`returns ${type}`, () => {
- const change = {
- [type]: true,
- };
-
- expect(getDiffChangeType(change)).toBe(type);
- });
- });
- });
-
- describe('getDecorator', () => {
- ['added', 'removed', 'modified'].forEach((type) => {
- it(`returns with linesDecorationsClassName for ${type}`, () => {
- const change = {
- [type]: true,
- };
-
- expect(
- getDecorator(change).options.linesDecorationsClassName,
- ).toBe(`dirty-diff dirty-diff-${type}`);
- });
-
- it('returns with line numbers', () => {
- const change = {
- lineNumber: 1,
- endLineNumber: 2,
- [type]: true,
- };
-
- const range = getDecorator(change).range;
-
- expect(range.startLineNumber).toBe(1);
- expect(range.endLineNumber).toBe(2);
- expect(range.startColumn).toBe(1);
- expect(range.endColumn).toBe(1);
- });
- });
- });
-
- describe('attachModel', () => {
- it('adds change event callback', () => {
- spyOn(model, 'onChange');
-
- controller.attachModel(model);
-
- expect(model.onChange).toHaveBeenCalled();
- });
-
- it('calls throttledComputeDiff on change', () => {
- spyOn(controller, 'throttledComputeDiff');
-
- controller.attachModel(model);
-
- model.getModel().setValue('123');
-
- expect(controller.throttledComputeDiff).toHaveBeenCalled();
- });
- });
-
- describe('computeDiff', () => {
- it('posts to worker', () => {
- spyOn(controller.dirtyDiffWorker, 'postMessage');
-
- controller.computeDiff(model);
-
- expect(controller.dirtyDiffWorker.postMessage).toHaveBeenCalledWith({
- path: model.path,
- originalContent: '',
- newContent: '',
- });
- });
- });
-
- describe('reDecorate', () => {
- it('calls decorations controller decorate', () => {
- spyOn(controller.decorationsController, 'decorate');
-
- controller.reDecorate(model);
-
- expect(controller.decorationsController.decorate).toHaveBeenCalledWith(model);
- });
- });
-
- describe('decorate', () => {
- it('adds decorations into decorations controller', () => {
- spyOn(controller.decorationsController, 'addDecorations');
-
- controller.decorate({ data: { changes: [], path: 'path' } });
-
- expect(controller.decorationsController.addDecorations).toHaveBeenCalledWith('path', 'dirtyDiff', jasmine.anything());
- });
-
- it('adds decorations into editor', () => {
- const spy = spyOn(controller.decorationsController.editor.instance, 'deltaDecorations');
-
- controller.decorate({ data: { changes: computeDiff('123', '1234'), path: 'path' } });
-
- expect(spy).toHaveBeenCalledWith([], [{
- range: new monaco.Range(
- 1, 1, 1, 1,
- ),
- options: {
- isWholeLine: true,
- linesDecorationsClassName: 'dirty-diff dirty-diff-modified',
- },
- }]);
- });
- });
-
- describe('dispose', () => {
- it('calls disposable dispose', () => {
- spyOn(controller.disposable, 'dispose').and.callThrough();
-
- controller.dispose();
-
- expect(controller.disposable.dispose).toHaveBeenCalled();
- });
-
- it('terminates worker', () => {
- spyOn(controller.dirtyDiffWorker, 'terminate').and.callThrough();
-
- controller.dispose();
-
- expect(controller.dirtyDiffWorker.terminate).toHaveBeenCalled();
- });
-
- it('removes worker event listener', () => {
- spyOn(controller.dirtyDiffWorker, 'removeEventListener').and.callThrough();
-
- controller.dispose();
-
- expect(controller.dirtyDiffWorker.removeEventListener).toHaveBeenCalledWith('message', jasmine.anything());
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/diff/diff_spec.js b/spec/javascripts/repo/lib/diff/diff_spec.js
deleted file mode 100644
index 57f3ac3d365..00000000000
--- a/spec/javascripts/repo/lib/diff/diff_spec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import { computeDiff } from '~/ide/lib/diff/diff';
-
-describe('Multi-file editor library diff calculator', () => {
- describe('computeDiff', () => {
- it('returns empty array if no changes', () => {
- const diff = computeDiff('123', '123');
-
- expect(diff).toEqual([]);
- });
-
- describe('modified', () => {
- it('', () => {
- const diff = computeDiff('123', '1234')[0];
-
- expect(diff.added).toBeTruthy();
- expect(diff.modified).toBeTruthy();
- expect(diff.removed).toBeUndefined();
- });
-
- it('', () => {
- const diff = computeDiff('123\n123\n123', '123\n1234\n123')[0];
-
- expect(diff.added).toBeTruthy();
- expect(diff.modified).toBeTruthy();
- expect(diff.removed).toBeUndefined();
- expect(diff.lineNumber).toBe(2);
- });
- });
-
- describe('added', () => {
- it('', () => {
- const diff = computeDiff('123', '123\n123')[0];
-
- expect(diff.added).toBeTruthy();
- expect(diff.modified).toBeUndefined();
- expect(diff.removed).toBeUndefined();
- });
-
- it('', () => {
- const diff = computeDiff('123\n123\n123', '123\n123\n1234\n123')[0];
-
- expect(diff.added).toBeTruthy();
- expect(diff.modified).toBeUndefined();
- expect(diff.removed).toBeUndefined();
- expect(diff.lineNumber).toBe(3);
- });
- });
-
- describe('removed', () => {
- it('', () => {
- const diff = computeDiff('123', '')[0];
-
- expect(diff.added).toBeUndefined();
- expect(diff.modified).toBeUndefined();
- expect(diff.removed).toBeTruthy();
- });
-
- it('', () => {
- const diff = computeDiff('123\n123\n123', '123\n123')[0];
-
- expect(diff.added).toBeUndefined();
- expect(diff.modified).toBeTruthy();
- expect(diff.removed).toBeTruthy();
- expect(diff.lineNumber).toBe(2);
- });
- });
-
- it('includes line number of change', () => {
- const diff = computeDiff('123', '')[0];
-
- expect(diff.lineNumber).toBe(1);
- });
-
- it('includes end line number of change', () => {
- const diff = computeDiff('123', '')[0];
-
- expect(diff.endLineNumber).toBe(1);
- });
- });
-});
diff --git a/spec/javascripts/repo/lib/editor_options_spec.js b/spec/javascripts/repo/lib/editor_options_spec.js
deleted file mode 100644
index edbf5450dce..00000000000
--- a/spec/javascripts/repo/lib/editor_options_spec.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import editorOptions from '~/ide/lib/editor_options';
-
-describe('Multi-file editor library editor options', () => {
- it('returns an array', () => {
- expect(editorOptions).toEqual(jasmine.any(Array));
- });
-});
diff --git a/spec/javascripts/repo/lib/editor_spec.js b/spec/javascripts/repo/lib/editor_spec.js
deleted file mode 100644
index 8d51d48a782..00000000000
--- a/spec/javascripts/repo/lib/editor_spec.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
-import { file } from '../helpers';
-
-describe('Multi-file editor library', () => {
- let instance;
-
- beforeEach((done) => {
- monacoLoader(['vs/editor/editor.main'], () => {
- instance = editor.create(monaco);
-
- done();
- });
- });
-
- afterEach(() => {
- instance.dispose();
- });
-
- it('creates instance of editor', () => {
- expect(editor.editorInstance).not.toBeNull();
- });
-
- describe('createInstance', () => {
- let el;
-
- beforeEach(() => {
- el = document.createElement('div');
- });
-
- it('creates editor instance', () => {
- spyOn(instance.monaco.editor, 'create').and.callThrough();
-
- instance.createInstance(el);
-
- expect(instance.monaco.editor.create).toHaveBeenCalled();
- });
-
- it('creates dirty diff controller', () => {
- instance.createInstance(el);
-
- expect(instance.dirtyDiffController).not.toBeNull();
- });
- });
-
- describe('createModel', () => {
- it('calls model manager addModel', () => {
- spyOn(instance.modelManager, 'addModel');
-
- instance.createModel('FILE');
-
- expect(instance.modelManager.addModel).toHaveBeenCalledWith('FILE');
- });
- });
-
- describe('attachModel', () => {
- let model;
-
- beforeEach(() => {
- instance.createInstance(document.createElement('div'));
-
- model = instance.createModel(file());
- });
-
- it('sets the current model on the instance', () => {
- instance.attachModel(model);
-
- expect(instance.currentModel).toBe(model);
- });
-
- it('attaches the model to the current instance', () => {
- spyOn(instance.instance, 'setModel');
-
- instance.attachModel(model);
-
- expect(instance.instance.setModel).toHaveBeenCalledWith(model.getModel());
- });
-
- it('attaches the model to the dirty diff controller', () => {
- spyOn(instance.dirtyDiffController, 'attachModel');
-
- instance.attachModel(model);
-
- expect(instance.dirtyDiffController.attachModel).toHaveBeenCalledWith(model);
- });
-
- it('re-decorates with the dirty diff controller', () => {
- spyOn(instance.dirtyDiffController, 'reDecorate');
-
- instance.attachModel(model);
-
- expect(instance.dirtyDiffController.reDecorate).toHaveBeenCalledWith(model);
- });
- });
-
- describe('clearEditor', () => {
- it('resets the editor model', () => {
- instance.createInstance(document.createElement('div'));
-
- spyOn(instance.instance, 'setModel');
-
- instance.clearEditor();
-
- expect(instance.instance.setModel).toHaveBeenCalledWith(null);
- });
- });
-
- describe('dispose', () => {
- it('calls disposble dispose method', () => {
- spyOn(instance.disposable, 'dispose').and.callThrough();
-
- instance.dispose();
-
- expect(instance.disposable.dispose).toHaveBeenCalled();
- });
-
- it('resets instance', () => {
- instance.createInstance(document.createElement('div'));
-
- expect(instance.instance).not.toBeNull();
-
- instance.dispose();
-
- expect(instance.instance).toBeNull();
- });
- });
-});
diff --git a/spec/javascripts/repo/monaco_loader_spec.js b/spec/javascripts/repo/monaco_loader_spec.js
deleted file mode 100644
index b8ac36972aa..00000000000
--- a/spec/javascripts/repo/monaco_loader_spec.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import monacoContext from 'monaco-editor/dev/vs/loader';
-import monacoLoader from '~/ide/monaco_loader';
-
-describe('MonacoLoader', () => {
- it('calls require.config and exports require', () => {
- expect(monacoContext.require.getConfig()).toEqual(jasmine.objectContaining({
- paths: {
- vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
- },
- }));
- expect(monacoLoader).toBe(monacoContext.require);
- });
-});
diff --git a/spec/javascripts/repo/stores/actions/branch_spec.js b/spec/javascripts/repo/stores/actions/branch_spec.js
deleted file mode 100644
index 00d16fd790d..00000000000
--- a/spec/javascripts/repo/stores/actions/branch_spec.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import { resetStore } from '../../helpers';
-
-describe('Multi-file store branch actions', () => {
- afterEach(() => {
- resetStore(store);
- });
-
- describe('createNewBranch', () => {
- beforeEach(() => {
- spyOn(service, 'createBranch').and.returnValue(Promise.resolve({
- json: () => ({
- name: 'testing',
- }),
- }));
- spyOn(history, 'pushState');
-
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'testing';
- store.state.projects.abcproject = {
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
- });
-
- it('creates new branch', (done) => {
- store.dispatch('createNewBranch', 'master')
- .then(() => {
- expect(store.state.currentBranchId).toBe('testing');
- expect(service.createBranch).toHaveBeenCalledWith('abcproject', {
- branch: 'master',
- ref: 'testing',
- });
-
- done();
- })
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/actions/file_spec.js b/spec/javascripts/repo/stores/actions/file_spec.js
deleted file mode 100644
index e2d8f002e27..00000000000
--- a/spec/javascripts/repo/stores/actions/file_spec.js
+++ /dev/null
@@ -1,431 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import { file, resetStore } from '../../helpers';
-
-describe('Multi-file store file actions', () => {
- afterEach(() => {
- resetStore(store);
- });
-
- describe('closeFile', () => {
- let localFile;
- let getLastCommitDataSpy;
- let oldGetLastCommitData;
-
- beforeEach(() => {
- getLastCommitDataSpy = jasmine.createSpy('getLastCommitData');
- oldGetLastCommitData = store._actions.getLastCommitData; // eslint-disable-line
- store._actions.getLastCommitData = [getLastCommitDataSpy]; // eslint-disable-line
-
- localFile = file('testFile');
- localFile.active = true;
- localFile.opened = true;
- localFile.parentTreeUrl = 'parentTreeUrl';
-
- store.state.openFiles.push(localFile);
- });
-
- afterEach(() => {
- store._actions.getLastCommitData = oldGetLastCommitData; // eslint-disable-line
- });
-
- it('closes open files', (done) => {
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(localFile.opened).toBeFalsy();
- expect(localFile.active).toBeFalsy();
- expect(store.state.openFiles.length).toBe(0);
-
- done();
- }).catch(done.fail);
- });
-
- it('does not close file if has changed', (done) => {
- localFile.changed = true;
-
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(localFile.opened).toBeTruthy();
- expect(localFile.active).toBeTruthy();
- expect(store.state.openFiles.length).toBe(1);
-
- done();
- }).catch(done.fail);
- });
-
- it('does not close file if temp file', (done) => {
- localFile.tempFile = true;
-
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(localFile.opened).toBeTruthy();
- expect(localFile.active).toBeTruthy();
- expect(store.state.openFiles.length).toBe(1);
-
- done();
- }).catch(done.fail);
- });
-
- it('force closes a changed file', (done) => {
- localFile.changed = true;
-
- store.dispatch('closeFile', { file: localFile, force: true })
- .then(() => {
- expect(localFile.opened).toBeFalsy();
- expect(localFile.active).toBeFalsy();
- expect(store.state.openFiles.length).toBe(0);
-
- done();
- }).catch(done.fail);
- });
-
- it('sets next file as active', (done) => {
- const f = file('otherfile');
- store.state.openFiles.push(f);
-
- expect(f.active).toBeFalsy();
-
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(f.active).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('calls getLastCommitData', (done) => {
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(getLastCommitDataSpy).toHaveBeenCalled();
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('setFileActive', () => {
- let scrollToTabSpy;
- let oldScrollToTab;
-
- beforeEach(() => {
- scrollToTabSpy = jasmine.createSpy('scrollToTab');
- oldScrollToTab = store._actions.scrollToTab; // eslint-disable-line
- store._actions.scrollToTab = [scrollToTabSpy]; // eslint-disable-line
- });
-
- afterEach(() => {
- store._actions.scrollToTab = oldScrollToTab; // eslint-disable-line
- });
-
- it('calls scrollToTab', (done) => {
- store.dispatch('setFileActive', file('setThisActive'))
- .then(() => {
- expect(scrollToTabSpy).toHaveBeenCalled();
-
- done();
- }).catch(done.fail);
- });
-
- it('sets the file active', (done) => {
- const localFile = file('activeFile');
-
- store.dispatch('setFileActive', localFile)
- .then(() => {
- expect(localFile.active).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('returns early if file is already active', (done) => {
- const localFile = file('earlyActive');
- localFile.active = true;
-
- store.dispatch('setFileActive', localFile)
- .then(() => {
- expect(scrollToTabSpy).not.toHaveBeenCalled();
-
- done();
- }).catch(done.fail);
- });
-
- it('sets current active file to not active', (done) => {
- const localFile = file('currentActive');
- localFile.active = true;
- store.state.openFiles.push(localFile);
-
- store.dispatch('setFileActive', file('newActive'))
- .then(() => {
- expect(localFile.active).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
- it('resets location.hash for line highlighting', (done) => {
- location.hash = 'test';
-
- store.dispatch('setFileActive', file('otherActive'))
- .then(() => {
- expect(location.hash).not.toBe('test');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('getFileData', () => {
- let localFile;
-
- beforeEach(() => {
- spyOn(service, 'getFileData').and.returnValue(Promise.resolve({
- headers: {
- 'page-title': 'testing getFileData',
- },
- json: () => Promise.resolve({
- blame_path: 'blame_path',
- commits_path: 'commits_path',
- permalink: 'permalink',
- raw_path: 'raw_path',
- binary: false,
- html: '123',
- render_error: '',
- }),
- }));
-
- localFile = file('newCreate');
- localFile.url = 'getFileDataURL';
- });
-
- afterEach(() => {
- store.dispatch('closeFile', {
- file: localFile,
- force: true,
- });
- });
-
- it('calls the service', (done) => {
- store.dispatch('getFileData', localFile)
- .then(() => {
- expect(service.getFileData).toHaveBeenCalledWith('getFileDataURL');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets the file data', (done) => {
- store.dispatch('getFileData', localFile)
- .then(Vue.nextTick)
- .then(() => {
- expect(localFile.blamePath).toBe('blame_path');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets document title', (done) => {
- store.dispatch('getFileData', localFile)
- .then(() => {
- expect(document.title).toBe('testing getFileData');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets the file as active', (done) => {
- store.dispatch('getFileData', localFile)
- .then(Vue.nextTick)
- .then(() => {
- expect(localFile.active).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('adds the file to open files', (done) => {
- store.dispatch('getFileData', localFile)
- .then(Vue.nextTick)
- .then(() => {
- expect(store.state.openFiles.length).toBe(1);
- expect(store.state.openFiles[0].name).toBe(localFile.name);
-
- done();
- }).catch(done.fail);
- });
-
- it('toggles the file loading', (done) => {
- store.dispatch('getFileData', localFile)
- .then(() => {
- expect(localFile.loading).toBeTruthy();
-
- return Vue.nextTick();
- })
- .then(() => {
- expect(localFile.loading).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('getRawFileData', () => {
- let tmpFile;
-
- beforeEach(() => {
- spyOn(service, 'getRawFileData').and.returnValue(Promise.resolve('raw'));
-
- tmpFile = file('tmpFile');
- });
-
- it('calls getRawFileData service method', (done) => {
- store.dispatch('getRawFileData', tmpFile)
- .then(() => {
- expect(service.getRawFileData).toHaveBeenCalledWith(tmpFile);
-
- done();
- }).catch(done.fail);
- });
-
- it('updates file raw data', (done) => {
- store.dispatch('getRawFileData', tmpFile)
- .then(() => {
- expect(tmpFile.raw).toBe('raw');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('changeFileContent', () => {
- let tmpFile;
-
- beforeEach(() => {
- tmpFile = file('tmpFile');
- });
-
- it('updates file content', (done) => {
- store.dispatch('changeFileContent', {
- file: tmpFile,
- content: 'content',
- })
- .then(() => {
- expect(tmpFile.content).toBe('content');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('createTempFile', () => {
- let projectTree;
-
- beforeEach(() => {
- document.body.innerHTML += '<div class="flash-container"></div>';
-
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
-
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
-
- projectTree = store.state.trees['abcproject/mybranch'];
- });
-
- afterEach(() => {
- document.querySelector('.flash-container').remove();
- });
-
- it('creates temp file', (done) => {
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then((f) => {
- expect(f.tempFile).toBeTruthy();
- expect(store.state.trees['abcproject/mybranch'].tree.length).toBe(1);
-
- done();
- }).catch(done.fail);
- });
-
- it('adds tmp file to open files', (done) => {
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then((f) => {
- expect(store.state.openFiles.length).toBe(1);
- expect(store.state.openFiles[0].name).toBe(f.name);
-
- done();
- }).catch(done.fail);
- });
-
- it('sets tmp file as active', (done) => {
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then((f) => {
- expect(f.active).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('enters edit mode if file is not base64', (done) => {
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then(() => {
- expect(store.state.editMode).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('creates flash message is file already exists', (done) => {
- store.state.trees['abcproject/mybranch'].tree.push(file('test', '1', 'blob'));
-
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then(() => {
- expect(document.querySelector('.flash-alert')).not.toBeNull();
-
- done();
- }).catch(done.fail);
- });
-
- it('increases level of file', (done) => {
- store.state.trees['abcproject/mybranch'].level = 1;
-
- store.dispatch('createTempFile', {
- name: 'test',
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- }).then((f) => {
- expect(f.level).toBe(2);
-
- done();
- }).catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/actions/tree_spec.js b/spec/javascripts/repo/stores/actions/tree_spec.js
deleted file mode 100644
index 65351dbb7d9..00000000000
--- a/spec/javascripts/repo/stores/actions/tree_spec.js
+++ /dev/null
@@ -1,350 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import { file, resetStore } from '../../helpers';
-
-describe('Multi-file store tree actions', () => {
- let projectTree;
-
- const basicCallParameters = {
- endpoint: 'rootEndpoint',
- projectId: 'abcproject',
- branch: 'master',
- };
-
- beforeEach(() => {
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- web_url: '',
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
- });
-
- afterEach(() => {
- resetStore(store);
- });
-
- describe('getTreeData', () => {
- beforeEach(() => {
- spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
- headers: {
- 'page-title': 'test',
- },
- json: () => Promise.resolve({
- last_commit_path: 'last_commit_path',
- parent_tree_url: 'parent_tree_url',
- path: '/',
- trees: [{ name: 'tree' }],
- blobs: [{ name: 'blob' }],
- submodules: [{ name: 'submodule' }],
- }),
- }));
- });
-
- it('calls service getTreeData', (done) => {
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(service.getTreeData).toHaveBeenCalledWith('rootEndpoint');
-
- done();
- }).catch(done.fail);
- });
-
- it('adds data into tree', (done) => {
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- projectTree = store.state.trees['abcproject/master'];
- expect(projectTree.tree.length).toBe(3);
- expect(projectTree.tree[0].type).toBe('tree');
- expect(projectTree.tree[1].type).toBe('submodule');
- expect(projectTree.tree[2].type).toBe('blob');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets parent tree URL', (done) => {
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(store.state.parentTreeUrl).toBe('parent_tree_url');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets last commit path', (done) => {
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(store.state.trees['abcproject/master'].lastCommitPath).toBe('last_commit_path');
-
- done();
- }).catch(done.fail);
- });
-
- it('sets root if not currently at root', (done) => {
- store.state.isInitialRoot = false;
-
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(store.state.isInitialRoot).toBeTruthy();
- expect(store.state.isRoot).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('sets page title', (done) => {
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(document.title).toBe('test');
-
- done();
- }).catch(done.fail);
- });
-
- it('calls getLastCommitData if prevLastCommitPath is not null', (done) => {
- const getLastCommitDataSpy = jasmine.createSpy('getLastCommitData');
- const oldGetLastCommitData = store._actions.getLastCommitData; // eslint-disable-line
- store._actions.getLastCommitData = [getLastCommitDataSpy]; // eslint-disable-line
- store.state.prevLastCommitPath = 'test';
-
- store.dispatch('getTreeData', basicCallParameters)
- .then(() => {
- expect(getLastCommitDataSpy).toHaveBeenCalledWith(projectTree);
-
- store._actions.getLastCommitData = oldGetLastCommitData; // eslint-disable-line
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('toggleTreeOpen', () => {
- let oldGetTreeData;
- let getTreeDataSpy;
- let tree;
-
- beforeEach(() => {
- getTreeDataSpy = jasmine.createSpy('getTreeData');
-
- oldGetTreeData = store._actions.getTreeData; // eslint-disable-line
- store._actions.getTreeData = [getTreeDataSpy]; // eslint-disable-line
-
- tree = {
- projectId: 'abcproject',
- branchId: 'master',
- opened: false,
- tree: [],
- };
- });
-
- afterEach(() => {
- store._actions.getTreeData = oldGetTreeData; // eslint-disable-line
- });
-
- it('toggles the tree open', (done) => {
- store.dispatch('toggleTreeOpen', {
- endpoint: 'test',
- tree,
- }).then(() => {
- expect(tree.opened).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('calls getTreeData if tree is closed', (done) => {
- store.dispatch('toggleTreeOpen', {
- endpoint: 'test',
- tree,
- }).then(() => {
- expect(getTreeDataSpy).toHaveBeenCalledWith({
- projectId: 'abcproject',
- branch: 'master',
- endpoint: 'test',
- tree,
- });
-
- done();
- }).catch(done.fail);
- });
-
- it('resets entries tree', (done) => {
- Object.assign(tree, {
- opened: true,
- tree: ['a'],
- });
-
- store.dispatch('toggleTreeOpen', {
- endpoint: 'test',
- tree,
- }).then(() => {
- expect(tree.tree.length).toBe(0);
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('createTempTree', () => {
- beforeEach(() => {
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
- projectTree = store.state.trees['abcproject/mybranch'];
- });
-
- it('creates temp tree', (done) => {
- store.dispatch('createTempTree', {
- projectId: store.state.currentProjectId,
- branchId: store.state.currentBranchId,
- name: 'test',
- parent: projectTree,
- })
- .then(() => {
- expect(projectTree.tree[0].name).toBe('test');
- expect(projectTree.tree[0].type).toBe('tree');
-
- done();
- }).catch(done.fail);
- });
-
- it('creates new folder inside another tree', (done) => {
- const tree = {
- type: 'tree',
- name: 'testing',
- tree: [],
- };
-
- projectTree.tree.push(tree);
-
- store.dispatch('createTempTree', {
- projectId: store.state.currentProjectId,
- branchId: store.state.currentBranchId,
- name: 'testing/test',
- parent: projectTree,
- })
- .then(() => {
- expect(projectTree.tree[0].name).toBe('testing');
- expect(projectTree.tree[0].tree[0].tempFile).toBeTruthy();
- expect(projectTree.tree[0].tree[0].name).toBe('test');
- expect(projectTree.tree[0].tree[0].type).toBe('tree');
-
- done();
- }).catch(done.fail);
- });
-
- it('does not create new tree if already exists', (done) => {
- const tree = {
- type: 'tree',
- name: 'testing',
- endpoint: 'test',
- tree: [],
- };
-
- projectTree.tree.push(tree);
-
- store.dispatch('createTempTree', {
- projectId: store.state.currentProjectId,
- branchId: store.state.currentBranchId,
- name: 'testing/test',
- parent: projectTree,
- })
- .then(() => {
- expect(projectTree.tree[0].name).toBe('testing');
- expect(projectTree.tree[0].tempFile).toBeUndefined();
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('getLastCommitData', () => {
- beforeEach(() => {
- spyOn(service, 'getTreeLastCommit').and.returnValue(Promise.resolve({
- headers: {
- 'more-logs-url': null,
- },
- json: () => Promise.resolve([{
- type: 'tree',
- file_name: 'testing',
- commit: {
- message: 'commit message',
- authored_date: '123',
- },
- }]),
- }));
-
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
-
- projectTree = store.state.trees['abcproject/mybranch'];
- projectTree.tree.push(file('testing', '1', 'tree'));
- projectTree.lastCommitPath = 'lastcommitpath';
- });
-
- it('calls service with lastCommitPath', (done) => {
- store.dispatch('getLastCommitData', projectTree)
- .then(() => {
- expect(service.getTreeLastCommit).toHaveBeenCalledWith('lastcommitpath');
-
- done();
- }).catch(done.fail);
- });
-
- it('updates trees last commit data', (done) => {
- store.dispatch('getLastCommitData', projectTree)
- .then(Vue.nextTick)
- .then(() => {
- expect(projectTree.tree[0].lastCommit.message).toBe('commit message');
-
- done();
- }).catch(done.fail);
- });
-
- it('does not update entry if not found', (done) => {
- projectTree.tree[0].name = 'a';
-
- store.dispatch('getLastCommitData', projectTree)
- .then(Vue.nextTick)
- .then(() => {
- expect(projectTree.tree[0].lastCommit.message).not.toBe('commit message');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('updateDirectoryData', () => {
- it('adds data into tree', (done) => {
- const tree = {
- tree: [],
- };
- const data = {
- trees: [{ name: 'tree' }],
- submodules: [{ name: 'submodule' }],
- blobs: [{ name: 'blob' }],
- };
-
- store.dispatch('updateDirectoryData', {
- data,
- tree,
- }).then(() => {
- expect(tree.tree[0].name).toBe('tree');
- expect(tree.tree[0].type).toBe('tree');
- expect(tree.tree[1].name).toBe('submodule');
- expect(tree.tree[1].type).toBe('submodule');
- expect(tree.tree[2].name).toBe('blob');
- expect(tree.tree[2].type).toBe('blob');
-
- done();
- }).catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/actions_spec.js b/spec/javascripts/repo/stores/actions_spec.js
deleted file mode 100644
index f678967b092..00000000000
--- a/spec/javascripts/repo/stores/actions_spec.js
+++ /dev/null
@@ -1,432 +0,0 @@
-import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
-import store from '~/ide/stores';
-import service from '~/ide/services';
-import { resetStore, file } from '../helpers';
-
-describe('Multi-file store actions', () => {
- afterEach(() => {
- resetStore(store);
- });
-
- describe('redirectToUrl', () => {
- it('calls visitUrl', (done) => {
- spyOn(urlUtils, 'visitUrl');
-
- store.dispatch('redirectToUrl', 'test')
- .then(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith('test');
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('setInitialData', () => {
- it('commits initial data', (done) => {
- store.dispatch('setInitialData', { canCommit: true })
- .then(() => {
- expect(store.state.canCommit).toBeTruthy();
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('closeDiscardPopup', () => {
- it('closes the discard popup', (done) => {
- store.dispatch('closeDiscardPopup', false)
- .then(() => {
- expect(store.state.discardPopupOpen).toBeFalsy();
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('discardAllChanges', () => {
- beforeEach(() => {
- store.state.openFiles.push(file('discardAll'));
- store.state.openFiles[0].changed = true;
- });
- });
-
- describe('closeAllFiles', () => {
- beforeEach(() => {
- store.state.openFiles.push(file('closeAll'));
- store.state.openFiles[0].opened = true;
- });
-
- it('closes all open files', (done) => {
- store.dispatch('closeAllFiles')
- .then(() => {
- expect(store.state.openFiles.length).toBe(0);
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('toggleEditMode', () => {
- it('toggles edit mode', (done) => {
- store.state.editMode = true;
-
- store.dispatch('toggleEditMode')
- .then(() => {
- expect(store.state.editMode).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
- it('sets preview mode', (done) => {
- store.state.currentBlobView = 'repo-editor';
- store.state.editMode = true;
-
- store.dispatch('toggleEditMode')
- .then(Vue.nextTick)
- .then(() => {
- expect(store.state.currentBlobView).toBe('repo-preview');
-
- done();
- }).catch(done.fail);
- });
-
- it('opens discard popup if there are changed files', (done) => {
- store.state.editMode = true;
- store.state.openFiles.push(file('discardChanges'));
- store.state.openFiles[0].changed = true;
-
- store.dispatch('toggleEditMode')
- .then(() => {
- expect(store.state.discardPopupOpen).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('can force closed if there are changed files', (done) => {
- store.state.editMode = true;
-
- store.state.openFiles.push(file('forceClose'));
- store.state.openFiles[0].changed = true;
-
- store.dispatch('toggleEditMode', true)
- .then(() => {
- expect(store.state.discardPopupOpen).toBeFalsy();
- expect(store.state.editMode).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
- it('discards file changes', (done) => {
- const f = file('discard');
- store.state.editMode = true;
- store.state.openFiles.push(f);
- f.changed = true;
-
- store.dispatch('toggleEditMode', true)
- .then(Vue.nextTick)
- .then(() => {
- expect(f.changed).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('toggleBlobView', () => {
- it('sets edit mode view if in edit mode', (done) => {
- store.dispatch('toggleBlobView')
- .then(() => {
- expect(store.state.currentBlobView).toBe('repo-editor');
-
- done();
- })
- .catch(done.fail);
- });
-
- it('sets preview mode view if not in edit mode', (done) => {
- store.state.editMode = false;
-
- store.dispatch('toggleBlobView')
- .then(() => {
- expect(store.state.currentBlobView).toBe('repo-preview');
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('checkCommitStatus', () => {
- beforeEach(() => {
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
- });
-
- it('calls service', (done) => {
- spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }));
-
- store.dispatch('checkCommitStatus')
- .then(() => {
- expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns true if current ref does not equal returned ID', (done) => {
- spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }));
-
- store.dispatch('checkCommitStatus')
- .then((val) => {
- expect(val).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns false if current ref equals returned ID', (done) => {
- spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
- data: {
- commit: { id: '1' },
- },
- }));
-
- store.dispatch('checkCommitStatus')
- .then((val) => {
- expect(val).toBeFalsy();
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('commitChanges', () => {
- let payload;
-
- beforeEach(() => {
- spyOn(window, 'scrollTo');
-
- document.body.innerHTML += '<div class="flash-container"></div>';
-
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- web_url: 'webUrl',
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
-
- payload = {
- branch: 'master',
- };
- });
-
- afterEach(() => {
- document.querySelector('.flash-container').remove();
- });
-
- describe('success', () => {
- beforeEach(() => {
- spyOn(service, 'commit').and.returnValue(Promise.resolve({
- data: {
- id: '123456',
- short_id: '123',
- message: 'test message',
- committed_date: 'date',
- stats: {
- additions: '1',
- deletions: '2',
- },
- },
- }));
- });
-
- it('calls service', (done) => {
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- expect(service.commit).toHaveBeenCalledWith('abcproject', payload);
-
- done();
- }).catch(done.fail);
- });
-
- it('shows flash notice', (done) => {
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- const alert = document.querySelector('.flash-container');
-
- expect(alert.querySelector('.flash-notice')).not.toBeNull();
- expect(alert.textContent.trim()).toBe(
- 'Your changes have been committed. Commit 123 with 1 additions, 2 deletions.',
- );
-
- done();
- }).catch(done.fail);
- });
-
- it('adds commit data to changed files', (done) => {
- const changedFile = file('changed');
- const f = file('newfile');
- changedFile.changed = true;
-
- store.state.openFiles.push(changedFile, f);
-
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- expect(changedFile.lastCommit.message).toBe('test message');
- expect(f.lastCommit.message).not.toBe('test message');
-
- done();
- }).catch(done.fail);
- });
-
- it('scrolls to top of page', (done) => {
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- expect(window.scrollTo).toHaveBeenCalledWith(0, 0);
-
- done();
- }).catch(done.fail);
- });
-
- it('redirects to new merge request page', (done) => {
- spyOn(urlUtils, 'visitUrl');
-
- store.dispatch('commitChanges', { payload, newMr: true })
- .then(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith('webUrl/merge_requests/new?merge_request%5Bsource_branch%5D=master');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('failed', () => {
- beforeEach(() => {
- spyOn(service, 'commit').and.returnValue(Promise.resolve({
- data: {
- message: 'failed message',
- },
- }));
- });
-
- it('shows failed message', (done) => {
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- const alert = document.querySelector('.flash-container');
-
- expect(alert.textContent.trim()).toBe(
- 'failed message',
- );
-
- done();
- }).catch(done.fail);
- });
- });
- });
-
- describe('createTempEntry', () => {
- beforeEach(() => {
- store.state.trees['abcproject/mybranch'] = {
- tree: [],
- };
- store.state.projects.abcproject = {
- web_url: '',
- };
- });
-
- it('creates a temp tree', (done) => {
- const projectTree = store.state.trees['abcproject/mybranch'];
-
- store.dispatch('createTempEntry', {
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- name: 'test',
- type: 'tree',
- })
- .then(() => {
- const baseTree = projectTree.tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].tempFile).toBeTruthy();
- expect(baseTree[0].type).toBe('tree');
-
- done();
- })
- .catch(done.fail);
- });
-
- it('creates temp file', (done) => {
- const projectTree = store.state.trees['abcproject/mybranch'];
-
- store.dispatch('createTempEntry', {
- projectId: 'abcproject',
- branchId: 'mybranch',
- parent: projectTree,
- name: 'test',
- type: 'blob',
- })
- .then(() => {
- const baseTree = projectTree.tree;
- expect(baseTree.length).toBe(1);
- expect(baseTree[0].tempFile).toBeTruthy();
- expect(baseTree[0].type).toBe('blob');
-
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('popHistoryState', () => {
-
- });
-
- describe('scrollToTab', () => {
- it('focuses the current active element', (done) => {
- document.body.innerHTML += '<div id="tabs"><div class="active"><div class="repo-tab"></div></div></div>';
- const el = document.querySelector('.repo-tab');
- spyOn(el, 'focus');
-
- store.dispatch('scrollToTab')
- .then(() => {
- setTimeout(() => {
- expect(el.focus).toHaveBeenCalled();
-
- document.getElementById('tabs').remove();
-
- done();
- });
- })
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/getters_spec.js b/spec/javascripts/repo/stores/getters_spec.js
deleted file mode 100644
index d0d5934f29a..00000000000
--- a/spec/javascripts/repo/stores/getters_spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import * as getters from '~/ide/stores/getters';
-import state from '~/ide/stores/state';
-import { file } from '../helpers';
-
-describe('Multi-file store getters', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- describe('changedFiles', () => {
- it('returns a list of changed opened files', () => {
- localState.openFiles.push(file());
- localState.openFiles.push(file('changed'));
- localState.openFiles[1].changed = true;
-
- const changedFiles = getters.changedFiles(localState);
-
- expect(changedFiles.length).toBe(1);
- expect(changedFiles[0].name).toBe('changed');
- });
- });
-
- describe('activeFile', () => {
- it('returns the current active file', () => {
- localState.openFiles.push(file());
- localState.openFiles.push(file('active'));
- localState.openFiles[1].active = true;
-
- expect(getters.activeFile(localState).name).toBe('active');
- });
-
- it('returns undefined if no active files are found', () => {
- localState.openFiles.push(file());
- localState.openFiles.push(file('active'));
-
- expect(getters.activeFile(localState)).toBeNull();
- });
- });
-
- describe('activeFileExtension', () => {
- it('returns the file extension for the current active file', () => {
- localState.openFiles.push(file('active'));
- localState.openFiles[0].active = true;
- localState.openFiles[0].path = 'test.js';
-
- expect(getters.activeFileExtension(localState)).toBe('.js');
-
- localState.openFiles[0].path = 'test.es6.js';
-
- expect(getters.activeFileExtension(localState)).toBe('.js');
- });
- });
-
- describe('canEditFile', () => {
- beforeEach(() => {
- localState.onTopOfBranch = true;
- localState.canCommit = true;
-
- localState.openFiles.push(file());
- localState.openFiles[0].active = true;
- });
-
- it('returns true if user can commit and has open files', () => {
- expect(getters.canEditFile(localState)).toBeTruthy();
- });
-
- it('returns false if user can commit and has no open files', () => {
- localState.openFiles = [];
-
- expect(getters.canEditFile(localState)).toBeFalsy();
- });
-
- it('returns false if user can commit and active file is binary', () => {
- localState.openFiles[0].binary = true;
-
- expect(getters.canEditFile(localState)).toBeFalsy();
- });
-
- it('returns false if user cant commit', () => {
- localState.canCommit = false;
-
- expect(getters.canEditFile(localState)).toBeFalsy();
- });
- });
-
- describe('modifiedFiles', () => {
- it('returns a list of modified files', () => {
- localState.openFiles.push(file());
- localState.openFiles.push(file('changed'));
- localState.openFiles[1].changed = true;
-
- const modifiedFiles = getters.modifiedFiles(localState);
-
- expect(modifiedFiles.length).toBe(1);
- expect(modifiedFiles[0].name).toBe('changed');
- });
- });
-
- describe('addedFiles', () => {
- it('returns a list of added files', () => {
- localState.openFiles.push(file());
- localState.openFiles.push(file('added'));
- localState.openFiles[1].changed = true;
- localState.openFiles[1].tempFile = true;
-
- const modifiedFiles = getters.addedFiles(localState);
-
- expect(modifiedFiles.length).toBe(1);
- expect(modifiedFiles[0].name).toBe('added');
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/mutations/branch_spec.js b/spec/javascripts/repo/stores/mutations/branch_spec.js
deleted file mode 100644
index a7167537ef2..00000000000
--- a/spec/javascripts/repo/stores/mutations/branch_spec.js
+++ /dev/null
@@ -1,18 +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, 'master');
-
- expect(localState.currentBranchId).toBe('master');
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/mutations/file_spec.js b/spec/javascripts/repo/stores/mutations/file_spec.js
deleted file mode 100644
index 6e204ef0404..00000000000
--- a/spec/javascripts/repo/stores/mutations/file_spec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import mutations from '~/ide/stores/mutations/file';
-import state from '~/ide/stores/state';
-import { file } from '../../helpers';
-
-describe('Multi-file store file mutations', () => {
- let localState;
- let localFile;
-
- beforeEach(() => {
- localState = state();
- localFile = file();
- });
-
- describe('SET_FILE_ACTIVE', () => {
- it('sets the file active', () => {
- mutations.SET_FILE_ACTIVE(localState, {
- file: localFile,
- active: true,
- });
-
- expect(localFile.active).toBeTruthy();
- });
- });
-
- describe('TOGGLE_FILE_OPEN', () => {
- beforeEach(() => {
- mutations.TOGGLE_FILE_OPEN(localState, localFile);
- });
-
- it('adds into opened files', () => {
- expect(localFile.opened).toBeTruthy();
- expect(localState.openFiles.length).toBe(1);
- });
-
- it('removes from opened files', () => {
- mutations.TOGGLE_FILE_OPEN(localState, localFile);
-
- expect(localFile.opened).toBeFalsy();
- expect(localState.openFiles.length).toBe(0);
- });
- });
-
- describe('SET_FILE_DATA', () => {
- it('sets extra file data', () => {
- mutations.SET_FILE_DATA(localState, {
- data: {
- blame_path: 'blame',
- commits_path: 'commits',
- permalink: 'permalink',
- raw_path: 'raw',
- binary: true,
- html: 'html',
- render_error: 'render_error',
- },
- file: localFile,
- });
-
- expect(localFile.blamePath).toBe('blame');
- expect(localFile.commitsPath).toBe('commits');
- expect(localFile.permalink).toBe('permalink');
- expect(localFile.rawPath).toBe('raw');
- expect(localFile.binary).toBeTruthy();
- expect(localFile.html).toBe('html');
- expect(localFile.renderError).toBe('render_error');
- });
- });
-
- describe('SET_FILE_RAW_DATA', () => {
- it('sets raw data', () => {
- mutations.SET_FILE_RAW_DATA(localState, {
- file: localFile,
- raw: 'testing',
- });
-
- expect(localFile.raw).toBe('testing');
- });
- });
-
- describe('UPDATE_FILE_CONTENT', () => {
- beforeEach(() => {
- localFile.raw = 'test';
- });
-
- it('sets content', () => {
- mutations.UPDATE_FILE_CONTENT(localState, {
- file: localFile,
- content: 'test',
- });
-
- expect(localFile.content).toBe('test');
- });
-
- it('sets changed if content does not match raw', () => {
- mutations.UPDATE_FILE_CONTENT(localState, {
- file: localFile,
- content: 'testing',
- });
-
- expect(localFile.content).toBe('testing');
- expect(localFile.changed).toBeTruthy();
- });
- });
-
- describe('DISCARD_FILE_CHANGES', () => {
- beforeEach(() => {
- localFile.content = 'test';
- localFile.changed = true;
- });
-
- it('resets content and changed', () => {
- mutations.DISCARD_FILE_CHANGES(localState, localFile);
-
- expect(localFile.content).toBe('');
- expect(localFile.changed).toBeFalsy();
- });
- });
-
- describe('CREATE_TMP_FILE', () => {
- it('adds file into parent tree', () => {
- const f = file('tmpFile');
-
- mutations.CREATE_TMP_FILE(localState, {
- file: f,
- parent: localFile,
- });
-
- expect(localFile.tree.length).toBe(1);
- expect(localFile.tree[0].name).toBe(f.name);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/mutations/tree_spec.js b/spec/javascripts/repo/stores/mutations/tree_spec.js
deleted file mode 100644
index e6ca8ea139e..00000000000
--- a/spec/javascripts/repo/stores/mutations/tree_spec.js
+++ /dev/null
@@ -1,71 +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();
- });
-
- describe('TOGGLE_TREE_OPEN', () => {
- it('toggles tree open', () => {
- mutations.TOGGLE_TREE_OPEN(localState, localTree);
-
- expect(localTree.opened).toBeTruthy();
-
- mutations.TOGGLE_TREE_OPEN(localState, localTree);
-
- expect(localTree.opened).toBeFalsy();
- });
- });
-
- describe('SET_DIRECTORY_DATA', () => {
- const data = [{
- name: 'tree',
- },
- {
- name: 'submodule',
- },
- {
- name: 'blob',
- }];
-
- it('adds directory data', () => {
- mutations.SET_DIRECTORY_DATA(localState, {
- data,
- tree: localState,
- });
-
- expect(localState.tree.length).toBe(3);
- expect(localState.tree[0].name).toBe('tree');
- expect(localState.tree[1].name).toBe('submodule');
- expect(localState.tree[2].name).toBe('blob');
- });
- });
-
- describe('SET_PARENT_TREE_URL', () => {
- it('sets the parent tree url', () => {
- mutations.SET_PARENT_TREE_URL(localState, 'test');
-
- expect(localState.parentTreeUrl).toBe('test');
- });
- });
-
- describe('CREATE_TMP_TREE', () => {
- it('adds tree into parent tree', () => {
- const tmpEntry = file('tmpTree');
-
- mutations.CREATE_TMP_TREE(localState, {
- tmpEntry,
- parent: localTree,
- });
-
- expect(localTree.tree.length).toBe(1);
- expect(localTree.tree[0].name).toBe(tmpEntry.name);
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/mutations_spec.js b/spec/javascripts/repo/stores/mutations_spec.js
deleted file mode 100644
index 5fd8ad94972..00000000000
--- a/spec/javascripts/repo/stores/mutations_spec.js
+++ /dev/null
@@ -1,125 +0,0 @@
-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();
- });
-
- describe('SET_INITIAL_DATA', () => {
- it('sets all initial data', () => {
- mutations.SET_INITIAL_DATA(localState, {
- test: 'test',
- });
-
- expect(localState.test).toBe('test');
- });
- });
-
- describe('SET_PREVIEW_MODE', () => {
- it('sets currentBlobView to repo-preview', () => {
- mutations.SET_PREVIEW_MODE(localState);
-
- expect(localState.currentBlobView).toBe('repo-preview');
-
- localState.currentBlobView = 'testing';
-
- mutations.SET_PREVIEW_MODE(localState);
-
- expect(localState.currentBlobView).toBe('repo-preview');
- });
- });
-
- describe('SET_EDIT_MODE', () => {
- it('sets currentBlobView to repo-editor', () => {
- mutations.SET_EDIT_MODE(localState);
-
- expect(localState.currentBlobView).toBe('repo-editor');
-
- localState.currentBlobView = 'testing';
-
- mutations.SET_EDIT_MODE(localState);
-
- expect(localState.currentBlobView).toBe('repo-editor');
- });
- });
-
- describe('TOGGLE_LOADING', () => {
- it('toggles loading of entry', () => {
- mutations.TOGGLE_LOADING(localState, entry);
-
- expect(entry.loading).toBeTruthy();
-
- mutations.TOGGLE_LOADING(localState, entry);
-
- expect(entry.loading).toBeFalsy();
- });
- });
-
- describe('TOGGLE_EDIT_MODE', () => {
- it('toggles editMode', () => {
- mutations.TOGGLE_EDIT_MODE(localState);
-
- expect(localState.editMode).toBeFalsy();
-
- mutations.TOGGLE_EDIT_MODE(localState);
-
- expect(localState.editMode).toBeTruthy();
- });
- });
-
- describe('TOGGLE_DISCARD_POPUP', () => {
- it('sets discardPopupOpen', () => {
- mutations.TOGGLE_DISCARD_POPUP(localState, true);
-
- expect(localState.discardPopupOpen).toBeTruthy();
-
- mutations.TOGGLE_DISCARD_POPUP(localState, false);
-
- expect(localState.discardPopupOpen).toBeFalsy();
- });
- });
-
- describe('SET_ROOT', () => {
- it('sets isRoot & initialRoot', () => {
- mutations.SET_ROOT(localState, true);
-
- expect(localState.isRoot).toBeTruthy();
- expect(localState.isInitialRoot).toBeTruthy();
-
- mutations.SET_ROOT(localState, false);
-
- expect(localState.isRoot).toBeFalsy();
- expect(localState.isInitialRoot).toBeFalsy();
- });
- });
-
- describe('SET_LEFT_PANEL_COLLAPSED', () => {
- it('sets left panel collapsed', () => {
- mutations.SET_LEFT_PANEL_COLLAPSED(localState, true);
-
- expect(localState.leftPanelCollapsed).toBeTruthy();
-
- mutations.SET_LEFT_PANEL_COLLAPSED(localState, false);
-
- expect(localState.leftPanelCollapsed).toBeFalsy();
- });
- });
-
- describe('SET_RIGHT_PANEL_COLLAPSED', () => {
- it('sets right panel collapsed', () => {
- mutations.SET_RIGHT_PANEL_COLLAPSED(localState, true);
-
- expect(localState.rightPanelCollapsed).toBeTruthy();
-
- mutations.SET_RIGHT_PANEL_COLLAPSED(localState, false);
-
- expect(localState.rightPanelCollapsed).toBeFalsy();
- });
- });
-});
diff --git a/spec/javascripts/repo/stores/utils_spec.js b/spec/javascripts/repo/stores/utils_spec.js
deleted file mode 100644
index 89745a2029e..00000000000
--- a/spec/javascripts/repo/stores/utils_spec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import * as utils from '~/ide/stores/utils';
-import state from '~/ide/stores/state';
-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('treeList', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- it('returns flat tree list', () => {
- localState.trees = [];
- localState.trees['abcproject/mybranch'] = {
- tree: [],
- };
- const baseTree = localState.trees['abcproject/mybranch'].tree;
- baseTree.push(file('1'));
- baseTree[0].tree.push(file('2'));
- baseTree[0].tree[0].tree.push(file('3'));
-
- const treeList = utils.treeList(localState, 'abcproject/mybranch');
-
- expect(treeList.length).toBe(3);
- expect(treeList[1].name).toBe(baseTree[0].tree[0].name);
- expect(treeList[2].name).toBe(baseTree[0].tree[0].tree[0].name);
- });
- });
-
- describe('createTemp', () => {
- it('creates temp tree', () => {
- const tmp = utils.createTemp({
- name: 'test',
- path: 'test',
- type: 'tree',
- level: 0,
- changed: false,
- content: '',
- base64: '',
- });
-
- expect(tmp.tempFile).toBeTruthy();
- expect(tmp.icon).toBe('fa-folder');
- });
-
- it('creates temp file', () => {
- const tmp = utils.createTemp({
- name: 'test',
- path: 'test',
- type: 'blob',
- level: 0,
- changed: false,
- content: '',
- base64: '',
- });
-
- expect(tmp.tempFile).toBeTruthy();
- expect(tmp.icon).toBe('fa-file-text-o');
- });
- });
-
- describe('findIndexOfFile', () => {
- let localState;
-
- beforeEach(() => {
- localState = [{
- path: '1',
- }, {
- path: '2',
- }];
- });
-
- it('finds in the index of an entry by path', () => {
- const index = utils.findIndexOfFile(localState, {
- path: '2',
- });
-
- expect(index).toBe(1);
- });
- });
-
- describe('findEntry', () => {
- let localState;
-
- beforeEach(() => {
- localState = {
- tree: [{
- type: 'tree',
- name: 'test',
- }, {
- type: 'blob',
- name: 'file',
- }],
- };
- });
-
- it('returns an entry found by name', () => {
- const foundEntry = utils.findEntry(localState.tree, 'tree', 'test');
-
- expect(foundEntry.type).toBe('tree');
- expect(foundEntry.name).toBe('test');
- });
-
- it('returns undefined when no entry found', () => {
- const foundEntry = utils.findEntry(localState.tree, 'blob', 'test');
-
- expect(foundEntry).toBeUndefined();
- });
- });
-});
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 031efcf1291..53899e00b53 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -55,8 +55,8 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidNoteError)
end
- context 'because the note was commands only' do
- let!(:email_raw) { fixture_file("emails/commands_only_reply.eml") }
+ context 'because the note was update commands only' do
+ let!(:email_raw) { fixture_file("emails/update_commands_only_reply.eml") }
context 'and current user cannot update noteable' do
it 'raises a CommandsOnlyNoteError' do
@@ -70,13 +70,10 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
end
it 'does not raise an error' do
- expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy
-
# One system note is created for the 'close' event
expect { receiver.execute }.to change { noteable.notes.count }.by(1)
expect(noteable.reload).to be_closed
- expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy
end
end
end
@@ -85,15 +82,13 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
context 'when the note contains quick actions' do
let!(:email_raw) { fixture_file("emails/commands_in_reply.eml") }
- context 'and current user cannot update noteable' do
- it 'post a note and does not update the noteable' do
- expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy
-
- # One system note is created for the new note
- expect { receiver.execute }.to change { noteable.notes.count }.by(1)
+ context 'and current user cannot update the noteable' do
+ it 'only executes the commands that the user can perform' do
+ expect { receiver.execute }
+ .to change { noteable.notes.user.count }.by(1)
+ .and change { user.todos_pending_count }.from(0).to(1)
expect(noteable.reload).to be_open
- expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy
end
end
@@ -102,14 +97,14 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
project.add_developer(user)
end
- it 'post a note and updates the noteable' do
+ it 'posts a note and updates the noteable' do
expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy
- # One system note is created for the new note, one for the 'close' event
- expect { receiver.execute }.to change { noteable.notes.count }.by(2)
+ expect { receiver.execute }
+ .to change { noteable.notes.user.count }.by(1)
+ .and change { user.todos_pending_count }.from(0).to(1)
expect(noteable.reload).to be_closed
- expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy
end
end
end
diff --git a/spec/lib/gitlab/verify/lfs_objects_spec.rb b/spec/lib/gitlab/verify/lfs_objects_spec.rb
new file mode 100644
index 00000000000..64f3a9660e0
--- /dev/null
+++ b/spec/lib/gitlab/verify/lfs_objects_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Verify::LfsObjects do
+ include GitlabVerifyHelpers
+
+ it_behaves_like 'Gitlab::Verify::BatchVerifier subclass' do
+ let!(:objects) { create_list(:lfs_object, 3, :with_file) }
+ end
+
+ describe '#run_batches' do
+ let(:failures) { collect_failures }
+ let(:failure) { failures[lfs_object] }
+
+ let!(:lfs_object) { create(:lfs_object, :with_file, :correct_oid) }
+
+ it 'passes LFS objects with the correct file' do
+ expect(failures).to eq({})
+ end
+
+ it 'fails LFS objects with a missing file' do
+ FileUtils.rm_f(lfs_object.file.path)
+
+ expect(failures.keys).to contain_exactly(lfs_object)
+ expect(failure).to be_a(Errno::ENOENT)
+ expect(failure.to_s).to include(lfs_object.file.path)
+ end
+
+ it 'fails LFS objects with a mismatched oid' do
+ File.truncate(lfs_object.file.path, 0)
+
+ expect(failures.keys).to contain_exactly(lfs_object)
+ expect(failure.to_s).to include('Checksum mismatch')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/verify/uploads_spec.rb b/spec/lib/gitlab/verify/uploads_spec.rb
new file mode 100644
index 00000000000..6146ce61226
--- /dev/null
+++ b/spec/lib/gitlab/verify/uploads_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Gitlab::Verify::Uploads do
+ include GitlabVerifyHelpers
+
+ it_behaves_like 'Gitlab::Verify::BatchVerifier subclass' do
+ let(:projects) { create_list(:project, 3, :with_avatar) }
+ let!(:objects) { projects.flat_map(&:uploads) }
+ end
+
+ describe '#run_batches' do
+ let(:project) { create(:project, :with_avatar) }
+ let(:failures) { collect_failures }
+ let(:failure) { failures[upload] }
+
+ let!(:upload) { project.uploads.first }
+
+ it 'passes uploads with the correct file' do
+ expect(failures).to eq({})
+ end
+
+ it 'fails uploads with a missing file' do
+ FileUtils.rm_f(upload.absolute_path)
+
+ expect(failures.keys).to contain_exactly(upload)
+ expect(failure).to be_a(Errno::ENOENT)
+ expect(failure.to_s).to include(upload.absolute_path)
+ end
+
+ it 'fails uploads with a mismatched checksum' do
+ upload.update!(checksum: 'something incorrect')
+
+ expect(failures.keys).to contain_exactly(upload)
+ expect(failure.to_s).to include('Checksum mismatch')
+ end
+
+ it 'fails uploads with a missing precalculated checksum' do
+ upload.update!(checksum: '')
+
+ expect(failures.keys).to contain_exactly(upload)
+ expect(failure.to_s).to include('Checksum missing')
+ end
+ end
+end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 0ae26e87154..f5cff66de6d 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -57,32 +57,55 @@ describe Notes::CreateService do
end
end
- describe 'note with commands' do
- describe '/close, /label, /assign & /milestone' do
- let(:note_text) { %(HELLO\n/close\n/assign @#{user.username}\nWORLD) }
+ context 'note with commands' do
+ context 'as a user who can update the target' do
+ context '/close, /label, /assign & /milestone' do
+ let(:note_text) { %(HELLO\n/close\n/assign @#{user.username}\nWORLD) }
- it 'saves the note and does not alter the note text' do
- expect_any_instance_of(Issues::UpdateService).to receive(:execute).and_call_original
+ it 'saves the note and does not alter the note text' do
+ expect_any_instance_of(Issues::UpdateService).to receive(:execute).and_call_original
- note = described_class.new(project, user, opts.merge(note: note_text)).execute
+ note = described_class.new(project, user, opts.merge(note: note_text)).execute
- expect(note.note).to eq "HELLO\nWORLD"
+ expect(note.note).to eq "HELLO\nWORLD"
+ end
+ end
+
+ context '/merge with sha option' do
+ let(:note_text) { %(HELLO\n/merge\nWORLD) }
+ let(:params) { opts.merge(note: note_text, merge_request_diff_head_sha: 'sha') }
+
+ it 'saves the note and exectues merge command' do
+ note = described_class.new(project, user, params).execute
+
+ expect(note.note).to eq "HELLO\nWORLD"
+ end
end
end
- describe '/merge with sha option' do
- let(:note_text) { %(HELLO\n/merge\nWORLD) }
- let(:params) { opts.merge(note: note_text, merge_request_diff_head_sha: 'sha') }
+ context 'as a user who cannot update the target' do
+ let(:note_text) { "HELLO\n/todo\n/assign #{user.to_reference}\nWORLD" }
+ let(:note) { described_class.new(project, user, opts.merge(note: note_text)).execute }
- it 'saves the note and exectues merge command' do
- note = described_class.new(project, user, params).execute
+ before do
+ project.team.find_member(user.id).update!(access_level: Gitlab::Access::GUEST)
+ end
+
+ it 'applies commands the user can execute' do
+ expect { note }.to change { user.todos_pending_count }.from(0).to(1)
+ end
+
+ it 'does not apply commands the user cannot execute' do
+ expect { note }.not_to change { issue.assignees }
+ end
+ it 'saves the note' do
expect(note.note).to eq "HELLO\nWORLD"
end
end
end
- describe 'personal snippet note' do
+ context 'personal snippet note' do
subject { described_class.new(nil, user, params).execute }
let(:snippet) { create(:personal_snippet) }
@@ -103,7 +126,7 @@ describe Notes::CreateService do
end
end
- describe 'note with emoji only' do
+ context 'note with emoji only' do
it 'creates regular note' do
opts = {
note: ':smile: ',
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index 5eafe56c99d..b1e218821d2 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -165,31 +165,17 @@ describe Notes::QuickActionsService do
let(:note) { create(:note_on_issue, project: project) }
- context 'with no current_user' do
- it 'returns false' do
- expect(described_class.supported?(note, nil)).to be_falsy
- end
- end
-
- context 'when current_user cannot update the noteable' do
- it 'returns false' do
- user = create(:user)
-
- expect(described_class.supported?(note, user)).to be_falsy
- end
- end
-
- context 'when current_user can update the noteable' do
+ context 'with a note on an issue' do
it 'returns true' do
- expect(described_class.supported?(note, master)).to be_truthy
+ expect(described_class.supported?(note)).to be_truthy
end
+ end
- context 'with a note on a commit' do
- let(:note) { create(:note_on_commit, project: project) }
+ context 'with a note on a commit' do
+ let(:note) { create(:note_on_commit, project: project) }
- it 'returns false' do
- expect(described_class.supported?(note, nil)).to be_falsy
- end
+ it 'returns false' do
+ expect(described_class.supported?(note)).to be_falsy
end
end
end
@@ -201,7 +187,7 @@ describe Notes::QuickActionsService do
service = described_class.new(project, master)
note = create(:note_on_issue, project: project)
- expect(described_class).to receive(:supported?).with(note, master)
+ expect(described_class).to receive(:supported?).with(note)
service.supported?(note)
end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 2c20821ac3f..f61469f673d 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -127,7 +127,6 @@ shared_examples 'issuable record that supports quick actions in its description
it "does not close the #{issuable_type}" do
write_note("/close")
- expect(page).to have_content '/close'
expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_open
@@ -165,7 +164,6 @@ shared_examples 'issuable record that supports quick actions in its description
it "does not reopen the #{issuable_type}" do
write_note("/reopen")
- expect(page).to have_content '/reopen'
expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_closed
@@ -195,10 +193,9 @@ shared_examples 'issuable record that supports quick actions in its description
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
- it "does not reopen the #{issuable_type}" do
+ it "does not change the #{issuable_type} title" do
write_note("/title Awesome new title")
- expect(page).to have_content '/title'
expect(page).not_to have_content 'Commands applied'
expect(issuable.reload.title).not_to eq 'Awesome new title'
diff --git a/spec/support/gitlab_verify.rb b/spec/support/gitlab_verify.rb
new file mode 100644
index 00000000000..13e2e37624d
--- /dev/null
+++ b/spec/support/gitlab_verify.rb
@@ -0,0 +1,45 @@
+RSpec.shared_examples 'Gitlab::Verify::BatchVerifier subclass' do
+ describe 'batching' do
+ let(:first_batch) { objects[0].id..objects[0].id }
+ let(:second_batch) { objects[1].id..objects[1].id }
+ let(:third_batch) { objects[2].id..objects[2].id }
+
+ it 'iterates through objects in batches' do
+ expect(collect_ranges).to eq([first_batch, second_batch, third_batch])
+ end
+
+ it 'allows the starting ID to be specified' do
+ expect(collect_ranges(start: second_batch.first)).to eq([second_batch, third_batch])
+ end
+
+ it 'allows the finishing ID to be specified' do
+ expect(collect_ranges(finish: second_batch.last)).to eq([first_batch, second_batch])
+ end
+ end
+end
+
+module GitlabVerifyHelpers
+ def collect_ranges(args = {})
+ verifier = described_class.new(args.merge(batch_size: 1))
+
+ collect_results(verifier).map { |range, _| range }
+ end
+
+ def collect_failures
+ verifier = described_class.new(batch_size: 1)
+
+ out = {}
+
+ collect_results(verifier).map { |_, failures| out.merge!(failures) }
+
+ out
+ end
+
+ def collect_results(verifier)
+ out = []
+
+ verifier.run_batches { |*args| out << args }
+
+ out
+ end
+end
diff --git a/spec/tasks/gitlab/lfs/check_rake_spec.rb b/spec/tasks/gitlab/lfs/check_rake_spec.rb
new file mode 100644
index 00000000000..2610edf8bac
--- /dev/null
+++ b/spec/tasks/gitlab/lfs/check_rake_spec.rb
@@ -0,0 +1,28 @@
+require 'rake_helper'
+
+describe 'gitlab:lfs rake tasks' do
+ describe 'check' do
+ let!(:lfs_object) { create(:lfs_object, :with_file, :correct_oid) }
+
+ before do
+ Rake.application.rake_require('tasks/gitlab/lfs/check')
+ stub_env('VERBOSE' => 'true')
+ end
+
+ it 'outputs the integrity check for each batch' do
+ expect { run_rake_task('gitlab:lfs:check') }.to output(/Failures: 0/).to_stdout
+ end
+
+ it 'errors out about missing files on the file system' do
+ FileUtils.rm_f(lfs_object.file.path)
+
+ expect { run_rake_task('gitlab:lfs:check') }.to output(/No such file.*#{Regexp.quote(lfs_object.file.path)}/).to_stdout
+ end
+
+ it 'errors out about invalid checksum' do
+ File.truncate(lfs_object.file.path, 0)
+
+ expect { run_rake_task('gitlab:lfs:check') }.to output(/Checksum mismatch/).to_stdout
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/uploads_rake_spec.rb b/spec/tasks/gitlab/uploads/check_rake_spec.rb
index ac0005e51e0..5d597c66133 100644
--- a/spec/tasks/gitlab/uploads_rake_spec.rb
+++ b/spec/tasks/gitlab/uploads/check_rake_spec.rb
@@ -5,23 +5,24 @@ describe 'gitlab:uploads rake tasks' do
let!(:upload) { create(:upload, path: Rails.root.join('spec/fixtures/banana_sample.gif')) }
before do
- Rake.application.rake_require 'tasks/gitlab/uploads'
+ Rake.application.rake_require('tasks/gitlab/uploads/check')
+ stub_env('VERBOSE' => 'true')
end
- it 'outputs the integrity check for each uploaded file' do
- expect { run_rake_task('gitlab:uploads:check') }.to output(/Checking file \(#{upload.id}\): #{Regexp.quote(upload.absolute_path)}/).to_stdout
+ it 'outputs the integrity check for each batch' do
+ expect { run_rake_task('gitlab:uploads:check') }.to output(/Failures: 0/).to_stdout
end
it 'errors out about missing files on the file system' do
- create(:upload)
+ missing_upload = create(:upload)
- expect { run_rake_task('gitlab:uploads:check') }.to output(/File does not exist on the file system/).to_stdout
+ expect { run_rake_task('gitlab:uploads:check') }.to output(/No such file.*#{Regexp.quote(missing_upload.absolute_path)}/).to_stdout
end
it 'errors out about invalid checksum' do
upload.update_column(:checksum, '01a3156db2cf4f67ec823680b40b7302f89ab39179124ad219f94919b8a1769e')
- expect { run_rake_task('gitlab:uploads:check') }.to output(/File checksum \(9e697aa09fe196909813ee36103e34f721fe47a5fdc8aac0e4e4ac47b9b38282\) does not match the one in the database \(#{upload.checksum}\)/).to_stdout
+ expect { run_rake_task('gitlab:uploads:check') }.to output(/Checksum mismatch/).to_stdout
end
end
end