summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 09:08:11 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 09:08:11 +0000
commit1f5a2543e4daf21dd98d8ff0514781c403445c81 (patch)
tree22af0594a5de457ffb346c2259f9d30c3fd5479f
parent9bded6fb2268204757c35540fadef8e1b6351249 (diff)
downloadgitlab-ce-1f5a2543e4daf21dd98d8ff0514781c403445c81.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG-EE.md4
-rw-r--r--CHANGELOG.md11
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue20
-rw-r--r--app/assets/javascripts/design_management/pages/index.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue1
-rw-r--r--app/assets/javascripts/diffs/constants.js19
-rw-r--r--app/assets/javascripts/diffs/store/actions.js5
-rw-r--r--app/assets/javascripts/ide/commit_icon.js11
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue2
-rw-r--r--app/assets/javascripts/ide/stores/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/getters.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/clientside/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/getters.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/actions.js3
-rw-r--r--app/assets/javascripts/ide/utils.js11
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js2
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle_with_alerts.js13
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue1
-rw-r--r--app/assets/javascripts/pages/projects/environments/metrics/index.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/changed_file_icon.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/renamed.vue107
-rw-r--r--changelogs/unreleased/216728-actionview-template-error-undefined-method-pages_domain-for-pagesd.yml5
-rw-r--r--changelogs/unreleased/216851-graphql-externallypaginatedarrayconnection-can-return-incorrect-nu.yml5
-rw-r--r--changelogs/unreleased/216970-add-instance-to-service-if-missing.yml5
-rw-r--r--changelogs/unreleased/217602-file-uploads-on-local-storage-with-nil-secret-in-the-db-are-broken.yml5
-rw-r--r--changelogs/unreleased/cat-duplicate-ci-pipelines-index-215790.yml5
-rw-r--r--changelogs/unreleased/upgrade-renamed-diff-show-full-diff-for-renamed-files.yml5
-rw-r--r--danger/telemetry/Dangerfile15
-rw-r--r--doc/administration/geo/disaster_recovery/index.md4
-rw-r--r--doc/administration/geo/replication/troubleshooting.md2
-rw-r--r--doc/administration/operations/puma.md14
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/user/admin_area/merge_requests_approvals.md16
-rw-r--r--doc/user/packages/conan_repository/index.md6
-rw-r--r--doc/user/packages/container_registry/index.md6
-rw-r--r--doc/user/packages/dependency_proxy/index.md6
-rw-r--r--doc/user/packages/index.md6
-rw-r--r--doc/user/packages/maven_repository/index.md6
-rw-r--r--doc/user/packages/npm_registry/index.md6
-rw-r--r--doc/user/packages/nuget_repository/index.md6
-rw-r--r--doc/user/packages/pypi_repository/index.md6
-rw-r--r--doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md4
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/frontend/__mocks__/lodash/throttle.js4
-rw-r--r--spec/frontend/design_management/router_spec.js1
-rw-r--r--spec/frontend/diffs/store/actions_spec.js22
-rw-r--r--spec/frontend/helpers/dom_shims/element_scroll_to.js6
-rw-r--r--spec/frontend/helpers/dom_shims/index.js1
-rw-r--r--spec/frontend/ide/commit_icon_spec.js45
-rw-r--r--spec/frontend/ide/components/commit_sidebar/message_field_spec.js (renamed from spec/javascripts/ide/components/commit_sidebar/message_field_spec.js)6
-rw-r--r--spec/frontend/ide/components/jobs/detail_spec.js187
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js2
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js (renamed from spec/javascripts/ide/components/repo_editor_spec.js)63
-rw-r--r--spec/frontend/ide/stores/actions/merge_request_spec.js (renamed from spec/javascripts/ide/stores/actions/merge_request_spec.js)52
-rw-r--r--spec/frontend/ide/stores/actions/project_spec.js (renamed from spec/javascripts/ide/stores/actions/project_spec.js)59
-rw-r--r--spec/frontend/ide/stores/actions/tree_spec.js (renamed from spec/javascripts/ide/stores/actions/tree_spec.js)33
-rw-r--r--spec/frontend/ide/stores/actions_spec.js (renamed from spec/javascripts/ide/stores/actions_spec.js)72
-rw-r--r--spec/frontend/ide/stores/modules/commit/actions_spec.js (renamed from spec/javascripts/ide/stores/modules/commit/actions_spec.js)109
-rw-r--r--spec/frontend/ide/utils_spec.js46
-rw-r--r--spec/frontend/notes/components/diff_with_note_spec.js9
-rw-r--r--spec/frontend/vue_shared/components/diff_viewer/diff_viewer_spec.js30
-rw-r--r--spec/frontend/vue_shared/components/diff_viewer/viewers/renamed_spec.js283
-rw-r--r--spec/javascripts/ide/components/jobs/detail_spec.js184
-rw-r--r--spec/javascripts/ide/helpers.js1
-rw-r--r--spec/javascripts/ide/mock_data.js1
-rw-r--r--spec/javascripts/vue_shared/components/file_finder/index_spec.js2
-rw-r--r--spec/models/project_services/emails_on_push_service_spec.rb22
70 files changed, 1017 insertions, 615 deletions
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md
index b48dce65463..c63254a24b4 100644
--- a/CHANGELOG-EE.md
+++ b/CHANGELOG-EE.md
@@ -1,5 +1,9 @@
Please view this file on the master branch, on stable branches it's out of date.
+## 12.10.6 (2020-05-15)
+
+- No changes.
+
## 12.10.5 (2020-05-13)
### Fixed (1 change)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b0b8ba9c0a..2b9f26ef35a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 12.10.6 (2020-05-15)
+
+### Fixed (5 changes)
+
+- Fix duplicate index removal on ci_pipelines.project_id. !31043
+- Fix 500 on creating an invalid domains and verification. !31190
+- Fix incorrect number of errors returned when querying sentry errors. !31252
+- Add instance column to services table if it's missing. !31631
+- Fix incorrect regex used in FileUploader#extract_dynamic_path. !32271
+
+
## 12.10.5 (2020-05-13)
### Added (1 change)
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index 7ff3271394d..f4014e3cc55 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -156,6 +156,9 @@ export default {
},
mounted() {
Mousetrap.bind('esc', this.closeDesign);
+ this.trackEvent();
+ // We need to reset the active discussion when opening a new design
+ this.updateActiveDiscussion();
},
beforeDestroy() {
Mousetrap.unbind('esc', this.closeDesign);
@@ -279,23 +282,6 @@ export default {
});
},
},
- beforeRouteEnter(to, from, next) {
- next(vm => {
- vm.trackEvent();
- });
- },
- beforeRouteUpdate(to, from, next) {
- this.trackEvent();
- this.closeCommentForm();
- // We need to reset the active discussion when opening a new design
- this.updateActiveDiscussion();
- next();
- },
- beforeRouteLeave(to, from, next) {
- // We need to reset the active discussion when moving to design list view
- this.updateActiveDiscussion();
- next();
- },
createImageDiffNoteMutation,
DESIGNS_ROUTE_NAME,
};
diff --git a/app/assets/javascripts/design_management/pages/index.vue b/app/assets/javascripts/design_management/pages/index.vue
index 7d419bc3ded..922c800009f 100644
--- a/app/assets/javascripts/design_management/pages/index.vue
+++ b/app/assets/javascripts/design_management/pages/index.vue
@@ -318,6 +318,6 @@ export default {
</li>
</ol>
</div>
- <router-view />
+ <router-view :key="$route.fullPath" />
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 5656bfc4707..e3fb0650e45 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -128,6 +128,7 @@ export default {
<no-preview-viewer v-else-if="noPreview" />
<diff-viewer
v-else
+ :diff-file="diffFile"
:diff-mode="diffMode"
:diff-viewer-mode="diffViewerMode"
:new-path="diffFile.new_path"
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 40e1aec42ed..9269dacd582 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -61,3 +61,22 @@ export const DIFFS_PER_PAGE = 20;
export const DIFF_COMPARE_BASE_VERSION_INDEX = -1;
export const DIFF_COMPARE_HEAD_VERSION_INDEX = -2;
+
+// State machine states
+export const STATE_IDLING = 'idle';
+export const STATE_LOADING = 'loading';
+export const STATE_ERRORED = 'errored';
+
+// State machine transitions
+export const TRANSITION_LOAD_START = 'LOAD_START';
+export const TRANSITION_LOAD_ERROR = 'LOAD_ERROR';
+export const TRANSITION_LOAD_SUCCEED = 'LOAD_SUCCEED';
+export const TRANSITION_ACKNOWLEDGE_ERROR = 'ACKNOWLEDGE_ERROR';
+
+export const RENAMED_DIFF_TRANSITIONS = {
+ [`${STATE_IDLING}:${TRANSITION_LOAD_START}`]: STATE_LOADING,
+ [`${STATE_LOADING}:${TRANSITION_LOAD_ERROR}`]: STATE_ERRORED,
+ [`${STATE_LOADING}:${TRANSITION_LOAD_SUCCEED}`]: STATE_IDLING,
+ [`${STATE_ERRORED}:${TRANSITION_LOAD_START}`]: STATE_LOADING,
+ [`${STATE_ERRORED}:${TRANSITION_ACKNOWLEDGE_ERROR}`]: STATE_IDLING,
+};
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 1975d6996a5..085dd34179e 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -656,11 +656,6 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: diffFile.file_path, lines });
dispatch('startRenderDiffsQueue');
- })
- .catch(error => {
- dispatch('receiveFullDiffError', diffFile.file_path);
-
- throw error;
});
}
diff --git a/app/assets/javascripts/ide/commit_icon.js b/app/assets/javascripts/ide/commit_icon.js
new file mode 100644
index 00000000000..4984b5bb91d
--- /dev/null
+++ b/app/assets/javascripts/ide/commit_icon.js
@@ -0,0 +1,11 @@
+import { commitItemIconMap } from './constants';
+
+export default file => {
+ if (file.deleted) {
+ return commitItemIconMap.deleted;
+ } else if (file.tempFile && !file.prevPath) {
+ return commitItemIconMap.addition;
+ }
+
+ return commitItemIconMap.modified;
+};
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index e70e251c117..5ae44c0d9a8 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -4,7 +4,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { viewerTypes } from '../../constants';
-import { getCommitIconMap } from '../../utils';
+import getCommitIconMap from '../../commit_icon';
export default {
components: {
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index e32b5ac7bdc..479def40013 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -297,6 +297,3 @@ export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
export * from './actions/merge_request';
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index 5d0a8570906..d148d0cd993 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -161,6 +161,3 @@ export const canCreateMergeRequests = (state, getters) =>
export const canPushCode = (state, getters) =>
Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_PUSH_CODE]);
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/clientside/actions.js b/app/assets/javascripts/ide/stores/modules/clientside/actions.js
index eb3bcdff2ae..2bebf8b90ce 100644
--- a/app/assets/javascripts/ide/stores/modules/clientside/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/clientside/actions.js
@@ -8,5 +8,4 @@ export const pingUsage = ({ rootGetters }) => {
return axios.post(url);
};
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
+export default pingUsage;
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 592c7e15918..65e2726a976 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -234,6 +234,3 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
window.dispatchEvent(new Event('resize'));
});
};
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js
index 413c4b0110d..37f887bcf0a 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/getters.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js
@@ -59,6 +59,3 @@ export const shouldDisableNewMrOption = (state, getters, rootState, rootGetters)
export const shouldCreateMR = (state, getters) =>
state.shouldCreateMR && !getters.shouldDisableNewMrOption;
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
index 59ead8a3dcf..6b2c929cd44 100644
--- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
@@ -117,6 +117,3 @@ export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => {
dispatch('discardFileChanges', file.path, { root: true });
}
};
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/pane/actions.js b/app/assets/javascripts/ide/stores/modules/pane/actions.js
index a8fcdf539ec..b7cff368fe4 100644
--- a/app/assets/javascripts/ide/stores/modules/pane/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pane/actions.js
@@ -25,6 +25,3 @@ export const open = ({ state, commit }, view) => {
export const close = ({ commit }) => {
commit(types.SET_OPEN, false);
};
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
index 1ea2b199237..9d14b7f7d48 100644
--- a/app/assets/javascripts/ide/utils.js
+++ b/app/assets/javascripts/ide/utils.js
@@ -1,4 +1,3 @@
-import { commitItemIconMap } from './constants';
import { languages } from 'monaco-editor';
import { flatten } from 'lodash';
@@ -53,16 +52,6 @@ export function isTextFile(content, mimeType, fileName) {
return asciiRegex.test(content);
}
-export const getCommitIconMap = file => {
- if (file.deleted) {
- return commitItemIconMap.deleted;
- } else if (file.tempFile && !file.prevPath) {
- return commitItemIconMap.addition;
- }
-
- return commitItemIconMap.modified;
-};
-
export const createPathWithExt = p => {
const ext = p.lastIndexOf('.') >= 0 ? p.substring(p.lastIndexOf('.') + 1) : '';
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 2bbf9ef9d78..5acb9ebc043 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -22,6 +22,8 @@ export default (props = {}) => {
props: {
...el.dataset,
currentDashboard,
+ customMetricsAvailable: parseBoolean(el.dataset.customMetricsAvailable),
+ prometheusAlertsAvailable: parseBoolean(el.dataset.prometheusAlertsAvailable),
hasMetrics: parseBoolean(el.dataset.hasMetrics),
...props,
},
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle_with_alerts.js b/app/assets/javascripts/monitoring/monitoring_bundle_with_alerts.js
deleted file mode 100644
index afe5ee0938d..00000000000
--- a/app/assets/javascripts/monitoring/monitoring_bundle_with_alerts.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { parseBoolean } from '~/lib/utils/common_utils';
-import initCeBundle from '~/monitoring/monitoring_bundle';
-
-export default () => {
- const el = document.getElementById('prometheus-graphs');
-
- if (el && el.dataset) {
- initCeBundle({
- customMetricsAvailable: parseBoolean(el.dataset.customMetricsAvailable),
- prometheusAlertsAvailable: parseBoolean(el.dataset.prometheusAlertsAvailable),
- });
- }
-};
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index cd5cfc09ea0..8897b54fac7 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -116,6 +116,7 @@ export default {
</div>
<div v-else>
<diff-viewer
+ :diff-file="discussion.diff_file"
:diff-mode="diffMode"
:diff-viewer-mode="diffViewerMode"
:new-path="discussion.diff_file.new_path"
diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js
index 31ec4e29ad2..0b644780ad4 100644
--- a/app/assets/javascripts/pages/projects/environments/metrics/index.js
+++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js
@@ -1,3 +1,3 @@
-import monitoringBundle from '~/monitoring/monitoring_bundle_with_alerts';
+import monitoringBundle from '~/monitoring/monitoring_bundle';
document.addEventListener('DOMContentLoaded', monitoringBundle);
diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
index 60e41a16854..7431b7e9ed4 100644
--- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
@@ -1,7 +1,7 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import { getCommitIconMap } from '~/ide/utils';
+import getCommitIconMap from '~/ide/commit_icon';
import { __ } from '~/locale';
export default {
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index bf3c3666300..a2fe19f9672 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -7,6 +7,10 @@ import ModeChanged from './viewers/mode_changed.vue';
export default {
props: {
+ diffFile: {
+ type: Object,
+ required: true,
+ },
diffMode: {
type: String,
required: true,
@@ -92,6 +96,7 @@ export default {
<div v-if="viewer" class="diff-file preview-container">
<component
:is="viewer"
+ :diff-file="diffFile"
:diff-mode="diffMode"
:new-path="fullNewPath"
:old-path="fullOldPath"
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/renamed.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/renamed.vue
index 5c1ea59b471..eba6dd4d14c 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/renamed.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/renamed.vue
@@ -1,3 +1,108 @@
+<script>
+import { mapActions } from 'vuex';
+import { GlAlert, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+
+import { __ } from '~/locale';
+import {
+ TRANSITION_LOAD_START,
+ TRANSITION_LOAD_ERROR,
+ TRANSITION_LOAD_SUCCEED,
+ TRANSITION_ACKNOWLEDGE_ERROR,
+ STATE_IDLING,
+ STATE_LOADING,
+ STATE_ERRORED,
+ RENAMED_DIFF_TRANSITIONS,
+} from '~/diffs/constants';
+import { truncateSha } from '~/lib/utils/text_utility';
+
+export default {
+ STATE_LOADING,
+ STATE_ERRORED,
+ TRANSITIONS: RENAMED_DIFF_TRANSITIONS,
+ uiText: {
+ showLink: __('Show file contents'),
+ commitLink: __('View file @ %{commitSha}'),
+ description: __('File renamed with no changes.'),
+ loadError: __('Unable to load file contents. Try again later.'),
+ },
+ components: {
+ GlAlert,
+ GlLink,
+ GlLoadingIcon,
+ GlSprintf,
+ },
+ props: {
+ diffFile: {
+ type: Object,
+ required: true,
+ },
+ },
+ data: () => ({
+ state: STATE_IDLING,
+ }),
+ computed: {
+ shortSha() {
+ return truncateSha(this.diffFile.content_sha);
+ },
+ canLoadFullDiff() {
+ return this.diffFile.alternate_viewer.name === 'text';
+ },
+ },
+ methods: {
+ ...mapActions('diffs', ['switchToFullDiffFromRenamedFile']),
+ transition(transitionEvent) {
+ const key = `${this.state}:${transitionEvent}`;
+
+ if (this.$options.TRANSITIONS[key]) {
+ this.state = this.$options.TRANSITIONS[key];
+ }
+ },
+ is(state) {
+ return this.state === state;
+ },
+ switchToFull() {
+ this.transition(TRANSITION_LOAD_START);
+
+ this.switchToFullDiffFromRenamedFile({ diffFile: this.diffFile })
+ .then(() => {
+ this.transition(TRANSITION_LOAD_SUCCEED);
+ })
+ .catch(() => {
+ this.transition(TRANSITION_LOAD_ERROR);
+ });
+ },
+ clickLink(event) {
+ if (this.canLoadFullDiff) {
+ event.preventDefault();
+
+ this.switchToFull();
+ }
+ },
+ dismissError() {
+ this.transition(TRANSITION_ACKNOWLEDGE_ERROR);
+ },
+ },
+};
+</script>
+
<template>
- <div class="nothing-here-block">{{ __('File moved') }}</div>
+ <div class="nothing-here-block">
+ <gl-loading-icon v-if="is($options.STATE_LOADING)" />
+ <template v-else>
+ <gl-alert
+ v-show="is($options.STATE_ERRORED)"
+ class="gl-mb-5 gl-text-left"
+ variant="danger"
+ @dismiss="dismissError"
+ >{{ $options.uiText.loadError }}</gl-alert
+ >
+ <span test-id="plaintext">{{ $options.uiText.description }}</span>
+ <gl-link :href="diffFile.view_path" @click="clickLink">
+ <span v-if="canLoadFullDiff">{{ $options.uiText.showLink }}</span>
+ <gl-sprintf v-else :message="$options.uiText.commitLink">
+ <template #commitSha>{{ shortSha }}</template>
+ </gl-sprintf>
+ </gl-link>
+ </template>
+ </div>
</template>
diff --git a/changelogs/unreleased/216728-actionview-template-error-undefined-method-pages_domain-for-pagesd.yml b/changelogs/unreleased/216728-actionview-template-error-undefined-method-pages_domain-for-pagesd.yml
deleted file mode 100644
index d34e092c9ee..00000000000
--- a/changelogs/unreleased/216728-actionview-template-error-undefined-method-pages_domain-for-pagesd.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 500 on creating an invalid domains and verification
-merge_request: 31190
-author:
-type: fixed
diff --git a/changelogs/unreleased/216851-graphql-externallypaginatedarrayconnection-can-return-incorrect-nu.yml b/changelogs/unreleased/216851-graphql-externallypaginatedarrayconnection-can-return-incorrect-nu.yml
deleted file mode 100644
index dd36d52f1c4..00000000000
--- a/changelogs/unreleased/216851-graphql-externallypaginatedarrayconnection-can-return-incorrect-nu.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix incorrect number of errors returned when querying sentry errors
-merge_request: 31252
-author:
-type: fixed
diff --git a/changelogs/unreleased/216970-add-instance-to-service-if-missing.yml b/changelogs/unreleased/216970-add-instance-to-service-if-missing.yml
deleted file mode 100644
index d8fcbb8f587..00000000000
--- a/changelogs/unreleased/216970-add-instance-to-service-if-missing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add instance column to services table if it's missing
-merge_request: 31631
-author:
-type: fixed
diff --git a/changelogs/unreleased/217602-file-uploads-on-local-storage-with-nil-secret-in-the-db-are-broken.yml b/changelogs/unreleased/217602-file-uploads-on-local-storage-with-nil-secret-in-the-db-are-broken.yml
deleted file mode 100644
index 48e585b59e8..00000000000
--- a/changelogs/unreleased/217602-file-uploads-on-local-storage-with-nil-secret-in-the-db-are-broken.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix incorrect regex used in FileUploader#extract_dynamic_path
-merge_request: 32271
-author:
-type: fixed
diff --git a/changelogs/unreleased/cat-duplicate-ci-pipelines-index-215790.yml b/changelogs/unreleased/cat-duplicate-ci-pipelines-index-215790.yml
deleted file mode 100644
index a21cafe5e14..00000000000
--- a/changelogs/unreleased/cat-duplicate-ci-pipelines-index-215790.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix duplicate index removal on ci_pipelines.project_id
-merge_request: 31043
-author:
-type: fixed
diff --git a/changelogs/unreleased/upgrade-renamed-diff-show-full-diff-for-renamed-files.yml b/changelogs/unreleased/upgrade-renamed-diff-show-full-diff-for-renamed-files.yml
new file mode 100644
index 00000000000..3a9cc4c383a
--- /dev/null
+++ b/changelogs/unreleased/upgrade-renamed-diff-show-full-diff-for-renamed-files.yml
@@ -0,0 +1,5 @@
+---
+title: Add a link to the `renamed` viewer to fully expand the renamed file (if it's text)
+merge_request: 28448
+author:
+type: added
diff --git a/danger/telemetry/Dangerfile b/danger/telemetry/Dangerfile
index c18a15fcb03..b749bd3b80b 100644
--- a/danger/telemetry/Dangerfile
+++ b/danger/telemetry/Dangerfile
@@ -1,12 +1,11 @@
# frozen_string_literal: true
TELEMETRY_CHANGED_FILES_MESSAGE = <<~MSG
-This merge request includes changes for which a review from the Data team and Telemetry team is recommended.
-Please reach out to @gitlab-org/growth/telemetry/engineers group for a review.
-MSG
+For the following files, a review from the [Data team and Telemetry team](https://gitlab.com/groups/gitlab-org/growth/telemetry/engineers/-/group_members?with_inherited_permissions=exclude) is recommended
+Please check the ~telemetry [guide](https://docs.gitlab.com/ee/development/telemetry/usage_ping.html) and reach out to @gitlab-org/growth/telemetry/engineers group for a review.
+
+%<changed_files>s
-USAGE_DATA_FILES_MESSAGE = <<~MSG
-For the following files, a review from the [Data team and Telemetry team](https://gitlab.com/groups/gitlab-org/growth/telemetry/engineers/-/group_members?with_inherited_permissions=exclude) is recommended:
MSG
tracking_files = [
@@ -16,7 +15,7 @@ tracking_files = [
'spec/helpers/tracking_helper_spec.rb',
'app/assets/javascripts/tracking.js',
'spec/frontend/tracking_spec.js'
- ]
+]
usage_data_changed_files = git.modified_files.grep(%r{usage_data})
snowplow_events_changed_files = git.modified_files & tracking_files
@@ -24,9 +23,7 @@ snowplow_events_changed_files = git.modified_files & tracking_files
changed_files = (usage_data_changed_files + snowplow_events_changed_files)
if changed_files.any?
- warn format(TELEMETRY_CHANGED_FILES_MESSAGE)
-
- markdown(USAGE_DATA_FILES_MESSAGE + helper.markdown_list(changed_files))
+ warn format(TELEMETRY_CHANGED_FILES_MESSAGE, changed_files: helper.markdown_list(changed_files))
telemetry_labels = ['telemetry']
telemetry_labels << 'telemetry::review pending' unless helper.mr_has_labels?('telemetry::reviewed')
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 9f7afad353b..3bf58798dca 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -125,10 +125,10 @@ Note the following when promoting a secondary:
previously for the **secondary** node.
1. If successful, the **secondary** node has now been promoted to the **primary** node.
-#### Promoting a **secondary** node with HA
+#### Promoting a **secondary** node with multiple servers
The `gitlab-ctl promote-to-primary-node` command cannot be used yet in
-conjunction with High Availability or with multiple machines, as it can only
+conjunction with multiple servers, as it can only
perform changes on a **secondary** with only a single machine. Instead, you must
do this manually.
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 293414a6e5e..37571486447 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -568,7 +568,7 @@ is displayed if you attempt to run this command on a primary node.
### Message: `sudo: gitlab-pg-ctl: command not found`
When
-[promoting a **secondary** node with HA](../disaster_recovery/index.md#promoting-a-secondary-node-with-ha),
+[promoting a **secondary** node with multiple servers](../disaster_recovery/index.md#promoting-a-secondary-node-with-multiple-servers),
you need to run the `gitlab-pg-ctl` command to promote the PostgreSQL
read-replica database.
diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md
index af559cf00e9..a53f83da8c0 100644
--- a/doc/administration/operations/puma.md
+++ b/doc/administration/operations/puma.md
@@ -10,7 +10,8 @@ Unicorn unless explicitly specified not to.
## Why switch to Puma?
Puma has a multi-thread architecture which uses less memory than a multi-process
-application server like Unicorn.
+application server like Unicorn. On GitLab.com, we saw a 40% reduction in memory
+consumption.
Most Rails applications requests normally include a proportion of I/O wait time.
During I/O wait time MRI Ruby will release the GVL (Global VM Lock) to other threads.
@@ -18,9 +19,14 @@ Multi-threaded Puma can therefore still serve more requests than a single proces
## Configuring Puma to replace Unicorn
-If you are currently running Unicorn and would like to switch to Puma, server configuration
-will _not_ carry over automatically. For details on matching Unicorn configuration settings with
-the Puma equivalent, where applicable, see [Converting Unicorn settings to Puma](https://docs.gitlab.com/omnibus/settings/puma.html#converting-unicorn-settings-to-puma).
+Beginning with GitLab 13.0, Puma is the default application server. We plan to remove support for
+Unicorn in GitLab 14.0.
+
+When switching to Puma, Unicorn server configuration
+will _not_ carry over automatically, due to differences between the two application servers.
+
+For Omnibus-based deployments, see [Configuring Puma Settings](https://docs.gitlab.com/omnibus/settings/puma.html#configuring-puma-settings). For Helm
+based deployments, see the [Webservice Chart documentation](https://docs.gitlab.com/charts/charts/gitlab/webservice/index.html).
## Performance caveat when using Puma with Rugged
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 09ad5f9afd7..174fff1c7cd 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -204,7 +204,7 @@ Omnibus GitLab defaults to the recommended Puma settings. Regardless of installa
tune the Puma settings.
If you're using Omnibus GitLab, see [Puma settings](https://docs.gitlab.com/omnibus/settings/puma.html)
-for instructions on changing the Puma settings.
+for instructions on changing the Puma settings. If you are using the GitLab Helm chart, see the [Webservice chart](https://docs.gitlab.com/charts/charts/gitlab/webservice/index.html).
### Puma workers
diff --git a/doc/user/admin_area/merge_requests_approvals.md b/doc/user/admin_area/merge_requests_approvals.md
index d0092b1eaf0..0c7beadad48 100644
--- a/doc/user/admin_area/merge_requests_approvals.md
+++ b/doc/user/admin_area/merge_requests_approvals.md
@@ -19,22 +19,6 @@ To enable merge request approval rules for an instance:
GitLab administrators can later override these settings in a project’s settings.
-## Merge request controls **(PREMIUM)**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/207250) in GitLab 13.0.
-
-Merge request approval settings, by default, are inherited by all projects in an instance.
-
-However, organizations with regulated projects may also have unregulated projects
-that should not inherit these same controls.
-
-Project-level merge request approval rules can now be edited by administrators.
-Project owners and maintainers can still view project-level merge request approval rules.
-
-In upcoming releases, we plan to provide a more holistic experience to scope instance-level merge request settings.
-For more information, review our plans to provide custom [approval settings for compliance-
-labeled projects](https://gitlab.com/gitlab-org/gitlab/-/issues/213601).
-
## Available rules
Merge request approval rules that can be set at an instance level are:
diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md
index a56a67d4959..288ee9bf1c9 100644
--- a/doc/user/packages/conan_repository/index.md
+++ b/doc/user/packages/conan_repository/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab Conan Repository **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/8248) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.6.
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index 5e642e1e21c..737470ccd62 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab Container Registry
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/4040) in GitLab 8.8.
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index be9710053dd..8df492cbc16 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# Dependency Proxy **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/7934) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11.
diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md
index 132a64d99a3..3209207156b 100644
--- a/doc/user/packages/index.md
+++ b/doc/user/packages/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab Package Registry
GitLab Packages allows organizations to utilize GitLab as a private repository
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index 51e62dc871e..0e71dcacd5f 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab Maven Repository **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/5811) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.3.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index b909646431b..ec96456b1ec 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab NPM Registry **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/5934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.7.
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index d9efb3239a8..057fbb852cb 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab NuGet Repository **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/20050) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.8.
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index 979f7a3c966..6f6609d82b1 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -1,3 +1,9 @@
+---
+stage: Package
+group: Package
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# GitLab PyPi Repository **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208747) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
diff --git a/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md b/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
index fdcb1049ef7..145ebb4a96a 100644
--- a/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
+++ b/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
@@ -82,6 +82,10 @@ to expand the entire file.
![Incrementally expand merge request diffs](img/incrementally_expand_merge_request_diffs_v12_2.png)
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205401) in GitLab 13.1, when viewing a
+merge request's **Changes** tab, if a certain file was only renamed, you can expand it to see the
+entire content by clicking **Show file contents**.
+
### Ignore whitespace changes in Merge Request diff view
If you click the **Hide whitespace changes** button, you can see the diff
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 14afe67bbfd..e59e767376c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9491,6 +9491,9 @@ msgstr ""
msgid "File name"
msgstr ""
+msgid "File renamed with no changes."
+msgstr ""
+
msgid "File sync capacity"
msgstr ""
@@ -14058,6 +14061,12 @@ msgstr ""
msgid "NetworkPolicies|Kubernetes error: %{error}"
msgstr ""
+msgid "NetworkPolicies|Policy %{policyName} was successfully changed"
+msgstr ""
+
+msgid "NetworkPolicies|Something went wrong, failed to update policy"
+msgstr ""
+
msgid "NetworkPolicies|Something went wrong, unable to fetch policies"
msgstr ""
@@ -19671,6 +19680,9 @@ msgstr ""
msgid "Show file browser"
msgstr ""
+msgid "Show file contents"
+msgstr ""
+
msgid "Show latest version"
msgstr ""
@@ -23061,6 +23073,9 @@ msgstr ""
msgid "Unable to generate new instance ID"
msgstr ""
+msgid "Unable to load file contents. Try again later."
+msgstr ""
+
msgid "Unable to load the diff"
msgstr ""
@@ -23983,6 +23998,9 @@ msgstr[1] ""
msgid "View file @ "
msgstr ""
+msgid "View file @ %{commitSha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
diff --git a/spec/frontend/__mocks__/lodash/throttle.js b/spec/frontend/__mocks__/lodash/throttle.js
new file mode 100644
index 00000000000..aef391afd0c
--- /dev/null
+++ b/spec/frontend/__mocks__/lodash/throttle.js
@@ -0,0 +1,4 @@
+// Similar to `lodash/debounce`, `lodash/throttle` also causes flaky specs.
+// See `./debounce.js` for more details.
+
+export default fn => fn;
diff --git a/spec/frontend/design_management/router_spec.js b/spec/frontend/design_management/router_spec.js
index 0f4afa5e288..d6488d3837a 100644
--- a/spec/frontend/design_management/router_spec.js
+++ b/spec/frontend/design_management/router_spec.js
@@ -33,6 +33,7 @@ function factory(routeArg) {
design: { loading: true },
permissions: { loading: true },
},
+ mutate: jest.fn(),
},
},
});
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 3fba661da44..b6038462170 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -1255,7 +1255,6 @@ describe('DiffsStoreActions', () => {
describe('switchToFullDiffFromRenamedFile', () => {
const SUCCESS_URL = 'fakehost/context.success';
- const ERROR_URL = 'fakehost/context.error';
const testFilePath = 'testpath';
const updatedViewerName = 'testviewer';
const preparedLine = { prepared: 'in-a-test' };
@@ -1311,27 +1310,6 @@ describe('DiffsStoreActions', () => {
},
);
});
-
- describe('error', () => {
- beforeEach(() => {
- renamedFile = { ...testFile, context_lines_path: ERROR_URL };
- mock.onGet(ERROR_URL).reply(500);
- });
-
- it('dispatches the error handling action', () => {
- const rejected = testAction(
- switchToFullDiffFromRenamedFile,
- { diffFile: renamedFile },
- null,
- [],
- [{ type: 'receiveFullDiffError', payload: testFilePath }],
- );
-
- return rejected.catch(error =>
- expect(error).toEqual(new Error('Request failed with status code 500')),
- );
- });
- });
});
describe('setFileCollapsed', () => {
diff --git a/spec/frontend/helpers/dom_shims/element_scroll_to.js b/spec/frontend/helpers/dom_shims/element_scroll_to.js
new file mode 100644
index 00000000000..68f8a115865
--- /dev/null
+++ b/spec/frontend/helpers/dom_shims/element_scroll_to.js
@@ -0,0 +1,6 @@
+Element.prototype.scrollTo = jest.fn().mockImplementation(function scrollTo(x, y) {
+ this.scrollLeft = x;
+ this.scrollTop = y;
+
+ this.dispatchEvent(new Event('scroll'));
+});
diff --git a/spec/frontend/helpers/dom_shims/index.js b/spec/frontend/helpers/dom_shims/index.js
index 17a2090d2f1..ed1c708c444 100644
--- a/spec/frontend/helpers/dom_shims/index.js
+++ b/spec/frontend/helpers/dom_shims/index.js
@@ -1,5 +1,6 @@
import './element_scroll_into_view';
import './element_scroll_by';
+import './element_scroll_to';
import './form_element';
import './get_client_rects';
import './inner_text';
diff --git a/spec/frontend/ide/commit_icon_spec.js b/spec/frontend/ide/commit_icon_spec.js
new file mode 100644
index 00000000000..90b8e34497c
--- /dev/null
+++ b/spec/frontend/ide/commit_icon_spec.js
@@ -0,0 +1,45 @@
+import { commitItemIconMap } from '~/ide/constants';
+import { decorateData } from '~/ide/stores/utils';
+import getCommitIconMap from '~/ide/commit_icon';
+
+const createFile = (name = 'name', id = name, type = '', parent = null) =>
+ decorateData({
+ id,
+ type,
+ icon: 'icon',
+ url: 'url',
+ name,
+ path: parent ? `${parent.path}/${name}` : name,
+ parentPath: parent ? parent.path : '',
+ lastCommit: {},
+ });
+
+describe('getCommitIconMap', () => {
+ let entry;
+
+ beforeEach(() => {
+ entry = createFile('Entry item');
+ });
+
+ it('renders "deleted" icon for deleted entries', () => {
+ entry.deleted = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.deleted);
+ });
+
+ it('renders "addition" icon for temp entries', () => {
+ entry.tempFile = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.addition);
+ });
+
+ it('renders "modified" icon for newly-renamed entries', () => {
+ entry.prevPath = 'foo/bar';
+ entry.tempFile = false;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
+ });
+
+ it('renders "modified" icon even for temp entries if they are newly-renamed', () => {
+ entry.prevPath = 'foo/bar';
+ entry.tempFile = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
+ });
+});
diff --git a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js b/spec/frontend/ide/components/commit_sidebar/message_field_spec.js
index 53508f52b2f..d6ea8b9a4bd 100644
--- a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/message_field_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
+import createComponent from 'helpers/vue_mount_component_helper';
import CommitMessageField from '~/ide/components/commit_sidebar/message_field.vue';
describe('IDE commit message field', () => {
@@ -54,7 +54,7 @@ describe('IDE commit message field', () => {
});
it('emits input event on input', () => {
- spyOn(vm, '$emit');
+ jest.spyOn(vm, '$emit').mockImplementation();
const textarea = vm.$el.querySelector('textarea');
textarea.value = 'testing';
@@ -160,7 +160,7 @@ describe('IDE commit message field', () => {
.then(() => {
expect(vm.scrollTop).toBe(50);
expect(vm.$el.querySelector('.highlights').style.transform).toBe(
- 'translate3d(0px, -50px, 0px)',
+ 'translate3d(0, -50px, 0)',
);
})
.then(done)
diff --git a/spec/frontend/ide/components/jobs/detail_spec.js b/spec/frontend/ide/components/jobs/detail_spec.js
new file mode 100644
index 00000000000..8f3815d5aab
--- /dev/null
+++ b/spec/frontend/ide/components/jobs/detail_spec.js
@@ -0,0 +1,187 @@
+import Vue from 'vue';
+import JobDetail from '~/ide/components/jobs/detail.vue';
+import { createStore } from '~/ide/stores';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { jobs } from '../../mock_data';
+import { TEST_HOST } from 'helpers/test_constants';
+
+describe('IDE jobs detail view', () => {
+ let vm;
+
+ const createComponent = () => {
+ const store = createStore();
+
+ store.state.pipelines.detailJob = {
+ ...jobs[0],
+ isLoading: true,
+ output: 'testing',
+ rawPath: `${TEST_HOST}/raw`,
+ };
+
+ return createComponentWithStore(Vue.extend(JobDetail), store);
+ };
+
+ beforeEach(() => {
+ vm = createComponent();
+
+ jest.spyOn(vm, 'fetchJobTrace').mockResolvedValue();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('mounted', () => {
+ beforeEach(() => {
+ vm = vm.$mount();
+ });
+
+ it('calls fetchJobTrace', () => {
+ expect(vm.fetchJobTrace).toHaveBeenCalled();
+ });
+
+ it('scrolls to bottom', () => {
+ expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalled();
+ });
+
+ it('renders job output', () => {
+ expect(vm.$el.querySelector('.bash').textContent).toContain('testing');
+ });
+
+ it('renders empty message output', done => {
+ vm.$store.state.pipelines.detailJob.output = '';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.bash').textContent).toContain('No messages were logged');
+
+ done();
+ });
+ });
+
+ it('renders loading icon', () => {
+ expect(vm.$el.querySelector('.build-loader-animation')).not.toBe(null);
+ expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('');
+ });
+
+ it('hides output when loading', () => {
+ expect(vm.$el.querySelector('.bash')).not.toBe(null);
+ expect(vm.$el.querySelector('.bash').style.display).toBe('none');
+ });
+
+ it('hide loading icon when isLoading is false', done => {
+ vm.$store.state.pipelines.detailJob.isLoading = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('none');
+
+ done();
+ });
+ });
+
+ it('resets detailJob when clicking header button', () => {
+ jest.spyOn(vm, 'setDetailJob').mockImplementation();
+
+ vm.$el.querySelector('.btn').click();
+
+ expect(vm.setDetailJob).toHaveBeenCalledWith(null);
+ });
+
+ it('renders raw path link', () => {
+ expect(vm.$el.querySelector('.controllers-buttons').getAttribute('href')).toBe(
+ `${TEST_HOST}/raw`,
+ );
+ });
+ });
+
+ describe('scroll buttons', () => {
+ beforeEach(() => {
+ vm = createComponent();
+ jest.spyOn(vm, 'fetchJobTrace').mockResolvedValue();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it.each`
+ fnName | btnName | scrollPos
+ ${'scrollDown'} | ${'down'} | ${0}
+ ${'scrollUp'} | ${'up'} | ${1}
+ `('triggers $fnName when clicking $btnName button', ({ fnName, scrollPos }) => {
+ jest.spyOn(vm, fnName).mockImplementation();
+
+ vm = vm.$mount();
+
+ vm.scrollPos = scrollPos;
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.btn-scroll:not([disabled])').click();
+ expect(vm[fnName]).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('scrollDown', () => {
+ beforeEach(() => {
+ vm = vm.$mount();
+
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTo').mockImplementation();
+ });
+
+ it('scrolls build trace to bottom', () => {
+ jest.spyOn(vm.$refs.buildTrace, 'scrollHeight', 'get').mockReturnValue(1000);
+
+ vm.scrollDown();
+
+ expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 1000);
+ });
+ });
+
+ describe('scrollUp', () => {
+ beforeEach(() => {
+ vm = vm.$mount();
+
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTo').mockImplementation();
+ });
+
+ it('scrolls build trace to top', () => {
+ vm.scrollUp();
+
+ expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 0);
+ });
+ });
+
+ describe('scrollBuildLog', () => {
+ beforeEach(() => {
+ vm = vm.$mount();
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTo').mockImplementation();
+ jest.spyOn(vm.$refs.buildTrace, 'offsetHeight', 'get').mockReturnValue(100);
+ jest.spyOn(vm.$refs.buildTrace, 'scrollHeight', 'get').mockReturnValue(200);
+ });
+
+ it('sets scrollPos to bottom when at the bottom', () => {
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTop', 'get').mockReturnValue(100);
+
+ vm.scrollBuildLog();
+
+ expect(vm.scrollPos).toBe(1);
+ });
+
+ it('sets scrollPos to top when at the top', () => {
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTop', 'get').mockReturnValue(0);
+ vm.scrollPos = 1;
+
+ vm.scrollBuildLog();
+
+ expect(vm.scrollPos).toBe(0);
+ });
+
+ it('resets scrollPos when not at top or bottom', () => {
+ jest.spyOn(vm.$refs.buildTrace, 'scrollTop', 'get').mockReturnValue(10);
+
+ vm.scrollBuildLog();
+
+ expect(vm.scrollPos).toBe('');
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index d909a5e478e..795ded35d20 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -6,7 +6,7 @@ import List from '~/ide/components/pipelines/list.vue';
import JobsList from '~/ide/components/jobs/list.vue';
import Tab from '~/vue_shared/components/tabs/tab.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import { pipelines } from '../../../../javascripts/ide/mock_data';
+import { pipelines } from 'jest/ide/mock_data';
import IDEServices from '~/ide/services';
const localVue = createLocalVue();
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 8db29011da7..af29e172332 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -7,13 +7,13 @@ import repoEditor from '~/ide/components/repo_editor.vue';
import Editor from '~/ide/lib/editor';
import { leftSidebarViews, FILE_VIEW_MODE_EDITOR, FILE_VIEW_MODE_PREVIEW } from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
-import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { file, resetStore } from '../helpers';
describe('RepoEditor', () => {
let vm;
- beforeEach(done => {
+ beforeEach(() => {
const f = {
...file(),
viewMode: FILE_VIEW_MODE_EDITOR,
@@ -45,12 +45,12 @@ describe('RepoEditor', () => {
Vue.set(vm.$store.state.entries, f.path, f);
- spyOn(vm, 'getFileData').and.returnValue(Promise.resolve());
- spyOn(vm, 'getRawFileData').and.returnValue(Promise.resolve());
+ jest.spyOn(vm, 'getFileData').mockResolvedValue();
+ jest.spyOn(vm, 'getRawFileData').mockResolvedValue();
vm.$mount();
- Vue.nextTick(() => setTimeout(done));
+ return vm.$nextTick();
});
afterEach(() => {
@@ -161,7 +161,7 @@ describe('RepoEditor', () => {
.then(() => {
vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')[1].click();
})
- .then(setTimeoutPromise)
+ .then(waitForPromises)
.then(() => {
expect(vm.$el.querySelector('.preview-container').innerHTML).toContain(
'<p>testing 123</p>',
@@ -186,7 +186,7 @@ describe('RepoEditor', () => {
describe('createEditorInstance', () => {
it('calls createInstance when viewer is editor', done => {
- spyOn(vm.editor, 'createInstance');
+ jest.spyOn(vm.editor, 'createInstance').mockImplementation();
vm.createEditorInstance();
@@ -200,7 +200,7 @@ describe('RepoEditor', () => {
it('calls createDiffInstance when viewer is diff', done => {
vm.$store.state.viewer = 'diff';
- spyOn(vm.editor, 'createDiffInstance');
+ jest.spyOn(vm.editor, 'createDiffInstance').mockImplementation();
vm.createEditorInstance();
@@ -214,7 +214,7 @@ describe('RepoEditor', () => {
it('calls createDiffInstance when viewer is a merge request diff', done => {
vm.$store.state.viewer = 'mrdiff';
- spyOn(vm.editor, 'createDiffInstance');
+ jest.spyOn(vm.editor, 'createDiffInstance').mockImplementation();
vm.createEditorInstance();
@@ -228,7 +228,7 @@ describe('RepoEditor', () => {
describe('setupEditor', () => {
it('creates new model', () => {
- spyOn(vm.editor, 'createModel').and.callThrough();
+ jest.spyOn(vm.editor, 'createModel');
Editor.editorInstance.modelManager.dispose();
@@ -239,7 +239,7 @@ describe('RepoEditor', () => {
});
it('attaches model to editor', () => {
- spyOn(vm.editor, 'attachModel').and.callThrough();
+ jest.spyOn(vm.editor, 'attachModel');
Editor.editorInstance.modelManager.dispose();
@@ -251,7 +251,7 @@ describe('RepoEditor', () => {
it('attaches model to merge request editor', () => {
vm.$store.state.viewer = 'mrdiff';
vm.file.mrChange = true;
- spyOn(vm.editor, 'attachMergeRequestModel');
+ jest.spyOn(vm.editor, 'attachMergeRequestModel').mockImplementation();
Editor.editorInstance.modelManager.dispose();
@@ -263,7 +263,7 @@ describe('RepoEditor', () => {
it('does not attach model to merge request editor when not a MR change', () => {
vm.$store.state.viewer = 'mrdiff';
vm.file.mrChange = false;
- spyOn(vm.editor, 'attachMergeRequestModel');
+ jest.spyOn(vm.editor, 'attachMergeRequestModel').mockImplementation();
Editor.editorInstance.modelManager.dispose();
@@ -273,7 +273,7 @@ describe('RepoEditor', () => {
});
it('adds callback methods', () => {
- spyOn(vm.editor, 'onPositionChange').and.callThrough();
+ jest.spyOn(vm.editor, 'onPositionChange');
Editor.editorInstance.modelManager.dispose();
@@ -286,7 +286,7 @@ describe('RepoEditor', () => {
it('updates state when model content changed', done => {
vm.model.setValue('testing 123\n');
- setTimeout(() => {
+ setImmediate(() => {
expect(vm.file.content).toBe('testing 123\n');
done();
@@ -294,7 +294,7 @@ describe('RepoEditor', () => {
});
it('sets head model as staged file', () => {
- spyOn(vm.editor, 'createModel').and.callThrough();
+ jest.spyOn(vm.editor, 'createModel');
Editor.editorInstance.modelManager.dispose();
@@ -310,8 +310,8 @@ describe('RepoEditor', () => {
describe('editor updateDimensions', () => {
beforeEach(() => {
- spyOn(vm.editor, 'updateDimensions').and.callThrough();
- spyOn(vm.editor, 'updateDiffView');
+ jest.spyOn(vm.editor, 'updateDimensions');
+ jest.spyOn(vm.editor, 'updateDiffView').mockImplementation();
});
it('calls updateDimensions when panelResizing is false', done => {
@@ -381,7 +381,7 @@ describe('RepoEditor', () => {
describe('when files view mode is preview', () => {
beforeEach(done => {
- spyOn(vm.editor, 'updateDimensions');
+ jest.spyOn(vm.editor, 'updateDimensions').mockImplementation();
vm.file.viewMode = FILE_VIEW_MODE_PREVIEW;
vm.$nextTick(done);
});
@@ -392,19 +392,12 @@ describe('RepoEditor', () => {
});
describe('when file view mode changes to editor', () => {
- beforeEach(done => {
+ it('should update dimensions', () => {
vm.file.viewMode = FILE_VIEW_MODE_EDITOR;
- // one tick to trigger watch
- vm.$nextTick()
- // another tick needed until we can update dimensions
- .then(() => vm.$nextTick())
- .then(done)
- .catch(done.fail);
- });
-
- it('should update dimensions', () => {
- expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ return vm.$nextTick().then(() => {
+ expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ });
});
});
});
@@ -412,8 +405,8 @@ describe('RepoEditor', () => {
describe('initEditor', () => {
beforeEach(() => {
vm.file.tempFile = false;
- spyOn(vm.editor, 'createInstance');
- spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
+ jest.spyOn(vm.editor, 'createInstance').mockImplementation();
+ jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
});
it('does not fetch file information for temp entries', done => {
@@ -459,12 +452,12 @@ describe('RepoEditor', () => {
describe('updates on file changes', () => {
beforeEach(() => {
- spyOn(vm, 'initEditor');
+ jest.spyOn(vm, 'initEditor').mockImplementation();
});
it('calls removePendingTab when old file is pending', done => {
- spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
- spyOn(vm, 'removePendingTab');
+ jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
+ jest.spyOn(vm, 'removePendingTab').mockImplementation();
vm.file.pending = true;
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/frontend/ide/stores/actions/merge_request_spec.js
index ce09cf51ac5..cb4eebd97d9 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/frontend/ide/stores/actions/merge_request_spec.js
@@ -1,7 +1,8 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
-import actions, {
+import createFlash from '~/flash';
+import {
getMergeRequestData,
getMergeRequestChanges,
getMergeRequestVersions,
@@ -14,6 +15,8 @@ import { resetStore } from '../../helpers';
const TEST_PROJECT = 'abcproject';
const TEST_PROJECT_ID = 17;
+jest.mock('~/flash');
+
describe('IDE store merge request actions', () => {
let mock;
@@ -41,7 +44,7 @@ describe('IDE store merge request actions', () => {
describe('base case', () => {
beforeEach(() => {
- spyOn(service, 'getProjectMergeRequests').and.callThrough();
+ jest.spyOn(service, 'getProjectMergeRequests');
mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, mockData);
});
@@ -66,7 +69,7 @@ describe('IDE store merge request actions', () => {
.dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' })
.then(() => {
expect(store.state.projects.abcproject.mergeRequests).toEqual({
- '2': jasmine.objectContaining(mrData),
+ '2': expect.objectContaining(mrData),
});
done();
})
@@ -99,7 +102,7 @@ describe('IDE store merge request actions', () => {
describe('no merge requests for branch available case', () => {
beforeEach(() => {
- spyOn(service, 'getProjectMergeRequests').and.callThrough();
+ jest.spyOn(service, 'getProjectMergeRequests');
mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, []);
});
@@ -122,16 +125,11 @@ describe('IDE store merge request actions', () => {
});
it('flashes message, if error', done => {
- const flashSpy = spyOnDependency(actions, 'flash');
-
store
.dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' })
- .then(() => {
- fail('Expected getMergeRequestsForBranch to throw an error');
- })
.catch(() => {
- expect(flashSpy).toHaveBeenCalled();
- expect(flashSpy.calls.argsFor(0)[0]).toEqual('Error fetching merge requests for bar');
+ expect(createFlash).toHaveBeenCalled();
+ expect(createFlash.mock.calls[0][0]).toBe('Error fetching merge requests for bar');
})
.then(done)
.catch(done.fail);
@@ -142,7 +140,7 @@ describe('IDE store merge request actions', () => {
describe('getMergeRequestData', () => {
describe('success', () => {
beforeEach(() => {
- spyOn(service, 'getProjectMergeRequestData').and.callThrough();
+ jest.spyOn(service, 'getProjectMergeRequestData');
mock
.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1/)
@@ -181,7 +179,7 @@ describe('IDE store merge request actions', () => {
});
it('dispatches error action', done => {
- const dispatch = jasmine.createSpy('dispatch');
+ const dispatch = jest.fn();
getMergeRequestData(
{
@@ -195,7 +193,7 @@ describe('IDE store merge request actions', () => {
.catch(() => {
expect(dispatch).toHaveBeenCalledWith('setErrorMessage', {
text: 'An error occurred while loading the merge request.',
- action: jasmine.any(Function),
+ action: expect.any(Function),
actionText: 'Please try again',
actionPayload: {
projectId: TEST_PROJECT,
@@ -217,7 +215,7 @@ describe('IDE store merge request actions', () => {
describe('success', () => {
beforeEach(() => {
- spyOn(service, 'getProjectMergeRequestChanges').and.callThrough();
+ jest.spyOn(service, 'getProjectMergeRequestChanges');
mock
.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/changes/)
@@ -254,7 +252,7 @@ describe('IDE store merge request actions', () => {
});
it('dispatches error action', done => {
- const dispatch = jasmine.createSpy('dispatch');
+ const dispatch = jest.fn();
getMergeRequestChanges(
{
@@ -268,7 +266,7 @@ describe('IDE store merge request actions', () => {
.catch(() => {
expect(dispatch).toHaveBeenCalledWith('setErrorMessage', {
text: 'An error occurred while loading the merge request changes.',
- action: jasmine.any(Function),
+ action: expect.any(Function),
actionText: 'Please try again',
actionPayload: {
projectId: TEST_PROJECT,
@@ -293,7 +291,7 @@ describe('IDE store merge request actions', () => {
mock
.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/versions/)
.reply(200, [{ id: 789 }]);
- spyOn(service, 'getProjectMergeRequestVersions').and.callThrough();
+ jest.spyOn(service, 'getProjectMergeRequestVersions');
});
it('calls getProjectMergeRequestVersions service method', done => {
@@ -324,7 +322,7 @@ describe('IDE store merge request actions', () => {
});
it('dispatches error action', done => {
- const dispatch = jasmine.createSpy('dispatch');
+ const dispatch = jest.fn();
getMergeRequestVersions(
{
@@ -338,7 +336,7 @@ describe('IDE store merge request actions', () => {
.catch(() => {
expect(dispatch).toHaveBeenCalledWith('setErrorMessage', {
text: 'An error occurred while loading the merge request version data.',
- action: jasmine.any(Function),
+ action: expect.any(Function),
actionText: 'Please try again',
actionPayload: {
projectId: TEST_PROJECT,
@@ -400,7 +398,7 @@ describe('IDE store merge request actions', () => {
const originalDispatch = store.dispatch;
- spyOn(store, 'dispatch').and.callFake((type, payload) => {
+ jest.spyOn(store, 'dispatch').mockImplementation((type, payload) => {
switch (type) {
case 'getMergeRequestData':
return Promise.resolve(testMergeRequest);
@@ -415,7 +413,7 @@ describe('IDE store merge request actions', () => {
return originalDispatch(type, payload);
}
});
- spyOn(service, 'getFileData').and.callFake(() =>
+ jest.spyOn(service, 'getFileData').mockImplementation(() =>
Promise.resolve({
headers: {},
}),
@@ -425,7 +423,7 @@ describe('IDE store merge request actions', () => {
it('dispatches actions for merge request data', done => {
openMergeRequest({ state: store.state, dispatch: store.dispatch, getters: mockGetters }, mr)
.then(() => {
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['getMergeRequestData', mr],
['setCurrentBranchId', testMergeRequest.source_branch],
[
@@ -493,15 +491,11 @@ describe('IDE store merge request actions', () => {
});
it('flashes message, if error', done => {
- const flashSpy = spyOnDependency(actions, 'flash');
- store.dispatch.and.returnValue(Promise.reject());
+ store.dispatch.mockRejectedValue();
openMergeRequest(store, mr)
- .then(() => {
- fail('Expected openMergeRequest to throw an error');
- })
.catch(() => {
- expect(flashSpy).toHaveBeenCalledWith(jasmine.any(String));
+ expect(createFlash).toHaveBeenCalledWith(expect.any(String));
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/frontend/ide/stores/actions/project_spec.js
index e962224d1ad..1aaeebf19d4 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/frontend/ide/stores/actions/project_spec.js
@@ -1,5 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
+import { createStore } from '~/ide/stores';
+import router from '~/ide/ide_router';
import {
refreshLastCommitData,
showBranchNotFoundError,
@@ -9,10 +11,8 @@ import {
loadFile,
loadBranch,
} from '~/ide/stores/actions';
-import { createStore } from '~/ide/stores';
import service from '~/ide/services';
import api from '~/api';
-import router from '~/ide/ide_router';
import { resetStore } from '../../helpers';
import testAction from '../../../helpers/vuex_action_helper';
@@ -49,13 +49,11 @@ describe('IDE store project actions', () => {
},
},
};
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }),
- );
+ jest.spyOn(service, 'getBranchData').mockResolvedValue({
+ data: {
+ commit: { id: '123' },
+ },
+ });
});
it('calls the service', done => {
@@ -110,7 +108,7 @@ describe('IDE store project actions', () => {
type: 'setErrorMessage',
payload: {
text: "Branch <strong>master</strong> was not found in this project's repository.",
- action: jasmine.any(Function),
+ action: expect.any(Function),
actionText: 'Create branch',
actionPayload: 'master',
},
@@ -122,10 +120,12 @@ describe('IDE store project actions', () => {
});
describe('createNewBranchFromDefault', () => {
- it('calls API', done => {
- spyOn(api, 'createBranch').and.returnValue(Promise.resolve());
- spyOn(router, 'push');
+ beforeEach(() => {
+ jest.spyOn(api, 'createBranch').mockResolvedValue();
+ jest.spyOn(router, 'push').mockImplementation();
+ });
+ it('calls API', done => {
createNewBranchFromDefault(
{
state: {
@@ -151,9 +151,7 @@ describe('IDE store project actions', () => {
});
it('clears error message', done => {
- const dispatchSpy = jasmine.createSpy('dispatch');
- spyOn(api, 'createBranch').and.returnValue(Promise.resolve());
- spyOn(router, 'push');
+ const dispatchSpy = jest.fn().mockName('dispatch');
createNewBranchFromDefault(
{
@@ -177,9 +175,6 @@ describe('IDE store project actions', () => {
});
it('reloads window', done => {
- spyOn(api, 'createBranch').and.returnValue(Promise.resolve());
- spyOn(router, 'push');
-
createNewBranchFromDefault(
{
state: {
@@ -215,7 +210,7 @@ describe('IDE store project actions', () => {
payload: { entry: store.state.trees[`${TEST_PROJECT_ID}/master`], forceValue: false },
},
],
- jasmine.any(Object),
+ expect.any(Object),
done,
);
});
@@ -243,7 +238,7 @@ describe('IDE store project actions', () => {
'foo/bar': { pending: false },
},
});
- spyOn(store, 'dispatch');
+ jest.spyOn(store, 'dispatch').mockImplementation();
});
it('does nothing, if basePath is not given', () => {
@@ -264,15 +259,15 @@ describe('IDE store project actions', () => {
it('does not handle tree entry action, if entry is pending', () => {
loadFile(store, { basePath: 'foo/bar-pending/' });
- expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
+ expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', expect.anything());
});
it('creates a new temp file supplied via URL if the file does not exist yet', () => {
loadFile(store, { basePath: 'not-existent.md' });
- expect(store.dispatch.calls.count()).toBe(1);
+ expect(store.dispatch.mock.calls).toHaveLength(1);
- expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
+ expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', expect.anything());
expect(store.dispatch).toHaveBeenCalledWith('createTempEntry', {
name: 'not-existent.md',
@@ -307,14 +302,14 @@ describe('IDE store project actions', () => {
it('fetches branch data', done => {
const mockGetters = { findBranch: () => ({ commit: { id: ref } }) };
- spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
loadBranch(
{ getters: mockGetters, state: store.state, dispatch: store.dispatch },
{ projectId, branchId },
)
.then(() => {
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['getBranchData', { projectId, branchId }],
['getMergeRequestsForBranch', { projectId, branchId }],
['getFiles', { projectId, branchId, ref }],
@@ -325,12 +320,12 @@ describe('IDE store project actions', () => {
});
it('shows an error if branch can not be fetched', done => {
- spyOn(store, 'dispatch').and.returnValue(Promise.reject());
+ jest.spyOn(store, 'dispatch').mockReturnValue(Promise.reject());
loadBranch(store, { projectId, branchId })
.then(done.fail)
.catch(() => {
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['getBranchData', { projectId, branchId }],
['showBranchNotFoundError', branchId],
]);
@@ -360,13 +355,13 @@ describe('IDE store project actions', () => {
describe('existing branch', () => {
beforeEach(() => {
- spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
});
it('dispatches branch actions', done => {
openBranch(store, branch)
.then(() => {
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['setCurrentBranchId', branchId],
['loadBranch', { projectId, branchId }],
['loadFile', { basePath: undefined }],
@@ -379,13 +374,13 @@ describe('IDE store project actions', () => {
describe('non-existent branch', () => {
beforeEach(() => {
- spyOn(store, 'dispatch').and.returnValue(Promise.reject());
+ jest.spyOn(store, 'dispatch').mockReturnValue(Promise.reject());
});
it('dispatches correct branch actions', done => {
openBranch(store, branch)
.then(val => {
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['setCurrentBranchId', branchId],
['loadBranch', { projectId, branchId }],
]);
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/frontend/ide/stores/actions/tree_spec.js
index 2201a3b4b57..37594512fe9 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/frontend/ide/stores/actions/tree_spec.js
@@ -1,5 +1,5 @@
import MockAdapter from 'axios-mock-adapter';
-import testAction from 'spec/helpers/vuex_action_helper';
+import testAction from 'helpers/vuex_action_helper';
import { showTreeEntry, getFiles, setDirectoryData } from '~/ide/stores/actions/tree';
import * as types from '~/ide/stores/mutation_types';
import axios from '~/lib/utils/axios_utils';
@@ -21,8 +21,7 @@ describe('Multi-file store tree actions', () => {
};
beforeEach(() => {
- jasmine.clock().install();
- spyOn(router, 'push');
+ jest.spyOn(router, 'push').mockImplementation();
mock = new MockAdapter(axios);
@@ -35,7 +34,6 @@ describe('Multi-file store tree actions', () => {
});
afterEach(() => {
- jasmine.clock().uninstall();
mock.restore();
resetStore(store);
});
@@ -43,7 +41,7 @@ describe('Multi-file store tree actions', () => {
describe('getFiles', () => {
describe('success', () => {
beforeEach(() => {
- spyOn(service, 'getFiles').and.callThrough();
+ jest.spyOn(service, 'getFiles');
mock
.onGet(/(.*)/)
@@ -54,15 +52,16 @@ describe('Multi-file store tree actions', () => {
]);
});
- it('calls service getFiles', done => {
- store
- .dispatch('getFiles', basicCallParameters)
- .then(() => {
- expect(service.getFiles).toHaveBeenCalledWith('foo/abcproject', '12345678');
-
- done();
- })
- .catch(done.fail);
+ it('calls service getFiles', () => {
+ return (
+ store
+ .dispatch('getFiles', basicCallParameters)
+ // getFiles actions calls lodash.defer
+ .then(() => jest.runOnlyPendingTimers())
+ .then(() => {
+ expect(service.getFiles).toHaveBeenCalledWith('foo/abcproject', '12345678');
+ })
+ );
});
it('adds data into tree', done => {
@@ -71,7 +70,7 @@ describe('Multi-file store tree actions', () => {
.then(() => {
// The populating of the tree is deferred for performance reasons.
// See this merge request for details: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/25700
- jasmine.clock().tick(1);
+ jest.advanceTimersByTime(1);
})
.then(() => {
projectTree = store.state.trees['abcproject/master'];
@@ -91,7 +90,7 @@ describe('Multi-file store tree actions', () => {
describe('error', () => {
it('dispatches error action', done => {
- const dispatch = jasmine.createSpy('dispatchSpy');
+ const dispatch = jest.fn();
store.state.projects = {
'abc/def': {
@@ -127,7 +126,7 @@ describe('Multi-file store tree actions', () => {
.catch(() => {
expect(dispatch).toHaveBeenCalledWith('setErrorMessage', {
text: 'An error occurred while loading all the files.',
- action: jasmine.any(Function),
+ action: expect.any(Function),
actionText: 'Please try again',
actionPayload: { projectId: 'abc/def', branchId: 'master-testing' },
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/frontend/ide/stores/actions_spec.js
index 364c8421b6b..d52b0435906 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/frontend/ide/stores/actions_spec.js
@@ -1,5 +1,8 @@
import MockAdapter from 'axios-mock-adapter';
-import actions, {
+import { visitUrl } from '~/lib/utils/url_utility';
+import { createStore } from '~/ide/stores';
+import router from '~/ide/ide_router';
+import {
stageAllChanges,
unstageAllChanges,
toggleFileFinder,
@@ -15,28 +18,29 @@ import actions, {
discardAllChanges,
} from '~/ide/stores/actions';
import axios from '~/lib/utils/axios_utils';
-import { createStore } from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types';
-import router from '~/ide/ide_router';
import { file } from '../helpers';
import testAction from '../../helpers/vuex_action_helper';
import eventHub from '~/ide/eventhub';
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+ joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
+}));
+
describe('Multi-file store actions', () => {
let store;
beforeEach(() => {
store = createStore();
- spyOn(store, 'commit').and.callThrough();
- spyOn(store, 'dispatch').and.callThrough();
- spyOn(router, 'push');
+ jest.spyOn(store, 'commit');
+ jest.spyOn(store, 'dispatch');
+ jest.spyOn(router, 'push').mockImplementation();
});
describe('redirectToUrl', () => {
it('calls visitUrl', done => {
- const visitUrl = spyOnDependency(actions, 'visitUrl');
-
store
.dispatch('redirectToUrl', 'test')
.then(() => {
@@ -79,7 +83,7 @@ describe('Multi-file store actions', () => {
discardAllChanges(store);
- expect(store.dispatch.calls.allArgs()).toEqual(jasmine.arrayContaining(expectedCalls));
+ expect(store.dispatch.mock.calls).toEqual(expect.arrayContaining(expectedCalls));
});
it('removes all files from changedFiles state', done => {
@@ -255,7 +259,7 @@ describe('Multi-file store actions', () => {
type: 'blob',
})
.then(() => {
- expect(store.state.stagedFiles).toEqual([jasmine.objectContaining({ name })]);
+ expect(store.state.stagedFiles).toEqual([expect.objectContaining({ name })]);
done();
})
@@ -311,12 +315,12 @@ describe('Multi-file store actions', () => {
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');
+ jest.spyOn(el, 'focus').mockImplementation();
store
.dispatch('scrollToTab')
.then(() => {
- setTimeout(() => {
+ setImmediate(() => {
expect(el.focus).toHaveBeenCalled();
document.getElementById('tabs').remove();
@@ -350,16 +354,16 @@ describe('Multi-file store actions', () => {
it('adds all files from changedFiles to stagedFiles', () => {
stageAllChanges(store);
- expect(store.commit.calls.allArgs()).toEqual([
+ expect(store.commit.mock.calls).toEqual([
[types.SET_LAST_COMMIT_MSG, ''],
- [types.STAGE_CHANGE, jasmine.objectContaining({ path: file1.path })],
+ [types.STAGE_CHANGE, expect.objectContaining({ path: file1.path })],
]);
});
it('opens pending tab if a change exists in that file', () => {
stageAllChanges(store);
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
[
'openPendingTab',
{ file: { ...file1, staged: true, changed: true }, keyPrefix: 'staged' },
@@ -382,15 +386,15 @@ describe('Multi-file store actions', () => {
it('removes all files from stagedFiles after unstaging', () => {
unstageAllChanges(store);
- expect(store.commit.calls.allArgs()).toEqual([
- [types.UNSTAGE_CHANGE, jasmine.objectContaining({ path: file2.path })],
+ expect(store.commit.mock.calls).toEqual([
+ [types.UNSTAGE_CHANGE, expect.objectContaining({ path: file2.path })],
]);
});
it('opens pending tab if a change exists in that file', () => {
unstageAllChanges(store);
- expect(store.dispatch.calls.allArgs()).toEqual([
+ expect(store.dispatch.mock.calls).toEqual([
['openPendingTab', { file: file1, keyPrefix: 'unstaged' }],
]);
});
@@ -696,7 +700,7 @@ describe('Multi-file store actions', () => {
describe('renameEntry', () => {
describe('purging of file model cache', () => {
beforeEach(() => {
- spyOn(eventHub, '$emit');
+ jest.spyOn(eventHub, '$emit').mockImplementation();
});
it('does not purge model cache for temporary entries that got renamed', done => {
@@ -715,9 +719,7 @@ describe('Multi-file store actions', () => {
name: 'new',
})
.then(() => {
- expect(eventHub.$emit.calls.allArgs()).not.toContain(
- 'editor.update.model.dispose.foo-bar',
- );
+ expect(eventHub.$emit.mock.calls).not.toContain('editor.update.model.dispose.foo-bar');
})
.then(done)
.catch(done.fail);
@@ -768,17 +770,17 @@ describe('Multi-file store actions', () => {
});
it('by default renames an entry and stages it', () => {
- const dispatch = jasmine.createSpy();
- const commit = jasmine.createSpy();
+ const dispatch = jest.fn();
+ const commit = jest.fn();
renameEntry(
{ dispatch, commit, state: store.state, getters: store.getters },
{ path: 'orig', name: 'renamed' },
);
- expect(commit.calls.allArgs()).toEqual([
+ expect(commit.mock.calls).toEqual([
[types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }],
- [types.STAGE_CHANGE, jasmine.objectContaining({ path: 'renamed' })],
+ [types.STAGE_CHANGE, expect.objectContaining({ path: 'renamed' })],
]);
});
@@ -813,7 +815,7 @@ describe('Multi-file store actions', () => {
renameEntry,
{ path: 'orig', name: 'renamed' },
store.state,
- [jasmine.objectContaining({ type: types.RENAME_ENTRY })],
+ [expect.objectContaining({ type: types.RENAME_ENTRY })],
[{ type: 'triggerFilesChange' }],
done,
);
@@ -831,7 +833,7 @@ describe('Multi-file store actions', () => {
name: 'renamed',
})
.then(() => {
- expect(router.push.calls.count()).toBe(1);
+ expect(router.push.mock.calls).toHaveLength(1);
expect(router.push).toHaveBeenCalledWith(`/project/foo-bar.md`);
})
.then(done)
@@ -918,7 +920,7 @@ describe('Multi-file store actions', () => {
expect(entries['new-folder']).toBeDefined();
expect(entries['new-folder/test']).toEqual(
- jasmine.objectContaining({
+ expect.objectContaining({
path: 'new-folder/test',
name: 'test',
prevPath: 'old-folder/test',
@@ -941,7 +943,7 @@ describe('Multi-file store actions', () => {
expect(entries['old-folder']).toBeDefined();
expect(entries['old-folder/test']).toEqual(
- jasmine.objectContaining({
+ expect.objectContaining({
path: 'old-folder/test',
name: 'test',
prevPath: undefined,
@@ -989,10 +991,10 @@ describe('Multi-file store actions', () => {
.dispatch('renameEntry', { path: filePath, name: fileName, parentPath: newParentPath })
.then(() => {
expect(store.state.entries[newParentPath]).toEqual(
- jasmine.objectContaining({
+ expect.objectContaining({
path: newParentPath,
type: 'tree',
- tree: jasmine.arrayContaining([
+ tree: expect.arrayContaining([
store.state.entries[`${newParentPath}/${fileName}`],
]),
}),
@@ -1078,7 +1080,7 @@ describe('Multi-file store actions', () => {
branchId: 'master-testing',
},
];
- dispatch = jasmine.createSpy('dispatchSpy');
+ dispatch = jest.fn();
document.body.innerHTML += '<div class="flash-container"></div>';
});
@@ -1092,7 +1094,7 @@ describe('Multi-file store actions', () => {
getBranchData(...callParams)
.then(done.fail)
.catch(e => {
- expect(dispatch.calls.count()).toEqual(0);
+ expect(dispatch.mock.calls).toHaveLength(0);
expect(e.response.status).toEqual(404);
expect(document.querySelector('.flash-alert')).toBeNull();
done();
@@ -1105,7 +1107,7 @@ describe('Multi-file store actions', () => {
getBranchData(...callParams)
.then(done.fail)
.catch(e => {
- expect(dispatch.calls.count()).toEqual(0);
+ expect(dispatch.mock.calls).toHaveLength(0);
expect(e.response).toBeUndefined();
expect(document.querySelector('.flash-alert')).not.toBeNull();
done();
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/frontend/ide/stores/modules/commit/actions_spec.js
index fb8cb300209..649c05441f6 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/frontend/ide/stores/modules/commit/actions_spec.js
@@ -1,5 +1,7 @@
-import { resetStore, file } from 'spec/ide/helpers';
-import rootActions from '~/ide/stores/actions';
+import { resetStore, file } from 'jest/ide/helpers';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import { visitUrl } from '~/lib/utils/url_utility';
import { createStore } from '~/ide/stores';
import service from '~/ide/services';
import router from '~/ide/ide_router';
@@ -10,15 +12,28 @@ import * as actions from '~/ide/stores/modules/commit/actions';
import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants';
import testAction from '../../../../helpers/vuex_action_helper';
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+ joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
+}));
+
const TEST_COMMIT_SHA = '123456789';
const store = createStore();
describe('IDE commit module actions', () => {
+ let mock;
+
beforeEach(() => {
- spyOn(router, 'push');
+ gon.api_version = 'v1';
+ mock = new MockAdapter(axios);
+ jest.spyOn(router, 'push').mockImplementation();
+
+ mock.onGet('/api/v1/projects/abcproject/repository/branches/master').reply(200);
});
afterEach(() => {
+ delete gon.api_version;
+ mock.restore();
resetStore(store);
});
@@ -71,7 +86,7 @@ describe('IDE commit module actions', () => {
[
{
type: mutationTypes.UPDATE_COMMIT_ACTION,
- payload: { commitAction: jasmine.anything() },
+ payload: { commitAction: expect.anything() },
},
{ type: mutationTypes.TOGGLE_SHOULD_CREATE_MR, payload: true },
],
@@ -92,7 +107,7 @@ describe('IDE commit module actions', () => {
[
{
type: mutationTypes.UPDATE_COMMIT_ACTION,
- payload: { commitAction: jasmine.anything() },
+ payload: { commitAction: expect.anything() },
},
{ type: mutationTypes.TOGGLE_SHOULD_CREATE_MR, payload: false },
],
@@ -168,7 +183,7 @@ describe('IDE commit module actions', () => {
let f;
beforeEach(() => {
- spyOn(eventHub, '$emit');
+ jest.spyOn(eventHub, '$emit').mockImplementation();
f = file('changedFile');
Object.assign(f, {
@@ -200,9 +215,9 @@ describe('IDE commit module actions', () => {
changed: true,
},
],
- openFiles: store.state.stagedFiles,
});
+ store.state.openFiles = store.state.stagedFiles;
store.state.stagedFiles.forEach(stagedFile => {
store.state.entries[stagedFile.path] = stagedFile;
});
@@ -280,11 +295,7 @@ describe('IDE commit module actions', () => {
});
describe('commitChanges', () => {
- let visitUrl;
-
beforeEach(() => {
- visitUrl = spyOnDependency(rootActions, 'visitUrl');
-
document.body.innerHTML += '<div class="flash-container"></div>';
const f = {
@@ -346,11 +357,7 @@ describe('IDE commit module actions', () => {
};
beforeEach(() => {
- spyOn(service, 'commit').and.returnValue(
- Promise.resolve({
- data: COMMIT_RESPONSE,
- }),
- );
+ jest.spyOn(service, 'commit').mockResolvedValue({ data: COMMIT_RESPONSE });
});
it('calls service', done => {
@@ -358,14 +365,14 @@ describe('IDE commit module actions', () => {
.dispatch('commit/commitChanges')
.then(() => {
expect(service.commit).toHaveBeenCalledWith('abcproject', {
- branch: jasmine.anything(),
+ branch: expect.anything(),
commit_message: 'testing 123',
actions: [
{
action: commitActionTypes.update,
- file_path: jasmine.anything(),
+ file_path: expect.anything(),
content: '\n',
- encoding: jasmine.anything(),
+ encoding: expect.anything(),
last_commit_id: undefined,
previous_path: undefined,
},
@@ -385,14 +392,14 @@ describe('IDE commit module actions', () => {
.dispatch('commit/commitChanges')
.then(() => {
expect(service.commit).toHaveBeenCalledWith('abcproject', {
- branch: jasmine.anything(),
+ branch: expect.anything(),
commit_message: 'testing 123',
actions: [
{
action: commitActionTypes.update,
- file_path: jasmine.anything(),
+ file_path: expect.anything(),
content: '\n',
- encoding: jasmine.anything(),
+ encoding: expect.anything(),
last_commit_id: TEST_COMMIT_SHA,
previous_path: undefined,
},
@@ -455,7 +462,7 @@ describe('IDE commit module actions', () => {
describe('merge request', () => {
it('redirects to new merge request page', done => {
- spyOn(eventHub, '$on');
+ jest.spyOn(eventHub, '$on').mockImplementation();
store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH;
store.state.commit.shouldCreateMR = true;
@@ -475,7 +482,7 @@ describe('IDE commit module actions', () => {
});
it('does not redirect to new merge request page when shouldCreateMR is not checked', done => {
- spyOn(eventHub, '$on');
+ jest.spyOn(eventHub, '$on').mockImplementation();
store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH;
store.state.commit.shouldCreateMR = false;
@@ -489,30 +496,25 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
- it('resets changed files before redirecting', done => {
- visitUrl = visitUrl.and.callFake(() => {
- expect(store.state.stagedFiles.length).toBe(0);
- done();
- });
-
- spyOn(eventHub, '$on');
+ it('resets changed files before redirecting', () => {
+ jest.spyOn(eventHub, '$on').mockImplementation();
store.state.commit.commitAction = '3';
- store.dispatch('commit/commitChanges').catch(done.fail);
+ return store.dispatch('commit/commitChanges').then(() => {
+ expect(store.state.stagedFiles.length).toBe(0);
+ });
});
});
});
describe('failed', () => {
beforeEach(() => {
- spyOn(service, 'commit').and.returnValue(
- Promise.resolve({
- data: {
- message: 'failed message',
- },
- }),
- );
+ jest.spyOn(service, 'commit').mockResolvedValue({
+ data: {
+ message: 'failed message',
+ },
+ });
});
it('shows failed message', done => {
@@ -543,20 +545,15 @@ describe('IDE commit module actions', () => {
};
it('commits TOGGLE_EMPTY_STATE mutation on empty repo', done => {
- spyOn(service, 'commit').and.returnValue(
- Promise.resolve({
- data: COMMIT_RESPONSE,
- }),
- );
-
- spyOn(store, 'commit').and.callThrough();
+ jest.spyOn(service, 'commit').mockResolvedValue({ data: COMMIT_RESPONSE });
+ jest.spyOn(store, 'commit');
store
.dispatch('commit/commitChanges')
.then(() => {
- expect(store.commit.calls.allArgs()).toEqual(
- jasmine.arrayContaining([
- ['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)],
+ expect(store.commit.mock.calls).toEqual(
+ expect.arrayContaining([
+ ['TOGGLE_EMPTY_STATE', expect.any(Object), expect.any(Object)],
]),
);
done();
@@ -566,19 +563,15 @@ describe('IDE commit module actions', () => {
it('does not commmit TOGGLE_EMPTY_STATE mutation on existing project', done => {
COMMIT_RESPONSE.parent_ids.push('1234');
- spyOn(service, 'commit').and.returnValue(
- Promise.resolve({
- data: COMMIT_RESPONSE,
- }),
- );
- spyOn(store, 'commit').and.callThrough();
+ jest.spyOn(service, 'commit').mockResolvedValue({ data: COMMIT_RESPONSE });
+ jest.spyOn(store, 'commit');
store
.dispatch('commit/commitChanges')
.then(() => {
- expect(store.commit.calls.allArgs()).not.toEqual(
- jasmine.arrayContaining([
- ['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)],
+ expect(store.commit.mock.calls).not.toEqual(
+ expect.arrayContaining([
+ ['TOGGLE_EMPTY_STATE', expect.any(Object), expect.any(Object)],
]),
);
done();
diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js
index ea975500e8d..f689314567a 100644
--- a/spec/frontend/ide/utils_spec.js
+++ b/spec/frontend/ide/utils_spec.js
@@ -1,6 +1,4 @@
-import { commitItemIconMap } from '~/ide/constants';
-import { getCommitIconMap, isTextFile, registerLanguages, trimPathComponents } from '~/ide/utils';
-import { decorateData } from '~/ide/stores/utils';
+import { isTextFile, registerLanguages, trimPathComponents } from '~/ide/utils';
import { languages } from 'monaco-editor';
describe('WebIDE utils', () => {
@@ -62,48 +60,6 @@ describe('WebIDE utils', () => {
});
});
- const createFile = (name = 'name', id = name, type = '', parent = null) =>
- decorateData({
- id,
- type,
- icon: 'icon',
- url: 'url',
- name,
- path: parent ? `${parent.path}/${name}` : name,
- parentPath: parent ? parent.path : '',
- lastCommit: {},
- });
-
- describe('getCommitIconMap', () => {
- let entry;
-
- beforeEach(() => {
- entry = createFile('Entry item');
- });
-
- it('renders "deleted" icon for deleted entries', () => {
- entry.deleted = true;
- expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.deleted);
- });
-
- it('renders "addition" icon for temp entries', () => {
- entry.tempFile = true;
- expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.addition);
- });
-
- it('renders "modified" icon for newly-renamed entries', () => {
- entry.prevPath = 'foo/bar';
- entry.tempFile = false;
- expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
- });
-
- it('renders "modified" icon even for temp entries if they are newly-renamed', () => {
- entry.prevPath = 'foo/bar';
- entry.tempFile = true;
- expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
- });
- });
-
describe('trimPathComponents', () => {
it.each`
input | output
diff --git a/spec/frontend/notes/components/diff_with_note_spec.js b/spec/frontend/notes/components/diff_with_note_spec.js
index d6d42e1988d..6480af015db 100644
--- a/spec/frontend/notes/components/diff_with_note_spec.js
+++ b/spec/frontend/notes/components/diff_with_note_spec.js
@@ -1,4 +1,4 @@
-import { mount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import DiffWithNote from '~/notes/components/diff_with_note.vue';
import { createStore } from '~/mr_notes/stores';
@@ -37,7 +37,7 @@ describe('diff_with_note', () => {
beforeEach(() => {
const diffDiscussion = getJSONFixture(discussionFixture)[0];
- wrapper = mount(DiffWithNote, {
+ wrapper = shallowMount(DiffWithNote, {
propsData: {
discussion: diffDiscussion,
},
@@ -76,7 +76,10 @@ describe('diff_with_note', () => {
describe('image diff', () => {
beforeEach(() => {
const imageDiscussion = getJSONFixture(imageDiscussionFixture)[0];
- wrapper = mount(DiffWithNote, { propsData: { discussion: imageDiscussion }, store });
+ wrapper = shallowMount(DiffWithNote, {
+ propsData: { discussion: imageDiscussion, diffFile: {} },
+ store,
+ });
});
it('shows image diff', () => {
diff --git a/spec/frontend/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/frontend/vue_shared/components/diff_viewer/diff_viewer_spec.js
index 636508be6b6..a6e4d812c3c 100644
--- a/spec/frontend/vue_shared/components/diff_viewer/diff_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/diff_viewer/diff_viewer_spec.js
@@ -8,6 +8,7 @@ describe('DiffViewer', () => {
const requiredProps = {
diffMode: 'replaced',
diffViewerMode: 'image',
+ diffFile: {},
newPath: GREEN_BOX_IMAGE_URL,
newSha: 'ABC',
oldPath: RED_BOX_IMAGE_URL,
@@ -71,16 +72,27 @@ describe('DiffViewer', () => {
});
});
- it('renders renamed component', () => {
- createComponent({
- ...requiredProps,
- diffMode: 'renamed',
- diffViewerMode: 'renamed',
- newPath: 'test.abc',
- oldPath: 'testold.abc',
+ describe('renamed file', () => {
+ it.each`
+ altViewer
+ ${'text'}
+ ${'notText'}
+ `('renders the renamed component when the alternate viewer is $altViewer', ({ altViewer }) => {
+ createComponent({
+ ...requiredProps,
+ diffFile: {
+ content_sha: '',
+ view_path: '',
+ alternate_viewer: { name: altViewer },
+ },
+ diffMode: 'renamed',
+ diffViewerMode: 'renamed',
+ newPath: 'test.abc',
+ oldPath: 'testold.abc',
+ });
+
+ expect(vm.$el.textContent).toContain('File renamed with no changes.');
});
-
- expect(vm.$el.textContent).toContain('File moved');
});
it('renders mode changed component', () => {
diff --git a/spec/frontend/vue_shared/components/diff_viewer/viewers/renamed_spec.js b/spec/frontend/vue_shared/components/diff_viewer/viewers/renamed_spec.js
new file mode 100644
index 00000000000..13584d7aeeb
--- /dev/null
+++ b/spec/frontend/vue_shared/components/diff_viewer/viewers/renamed_spec.js
@@ -0,0 +1,283 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
+import Renamed from '~/vue_shared/components/diff_viewer/viewers/renamed.vue';
+import {
+ TRANSITION_LOAD_START,
+ TRANSITION_LOAD_ERROR,
+ TRANSITION_LOAD_SUCCEED,
+ TRANSITION_ACKNOWLEDGE_ERROR,
+ STATE_IDLING,
+ STATE_LOADING,
+ STATE_ERRORED,
+} from '~/diffs/constants';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+function createRenamedComponent({
+ props = {},
+ methods = {},
+ store = new Vuex.Store({}),
+ deep = false,
+}) {
+ const mnt = deep ? mount : shallowMount;
+
+ return mnt(Renamed, {
+ propsData: { ...props },
+ localVue,
+ store,
+ methods,
+ });
+}
+
+describe('Renamed Diff Viewer', () => {
+ const DIFF_FILE_COMMIT_SHA = 'commitsha';
+ const DIFF_FILE_SHORT_SHA = 'commitsh';
+ const DIFF_FILE_VIEW_PATH = `blob/${DIFF_FILE_COMMIT_SHA}/filename.ext`;
+ let diffFile;
+ let wrapper;
+
+ beforeEach(() => {
+ diffFile = {
+ content_sha: DIFF_FILE_COMMIT_SHA,
+ view_path: DIFF_FILE_VIEW_PATH,
+ alternate_viewer: {
+ name: 'text',
+ },
+ };
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('is', () => {
+ beforeEach(() => {
+ wrapper = createRenamedComponent({ props: { diffFile } });
+ });
+
+ it.each`
+ state | request | result
+ ${'idle'} | ${'idle'} | ${true}
+ ${'idle'} | ${'loading'} | ${false}
+ ${'idle'} | ${'errored'} | ${false}
+ ${'loading'} | ${'loading'} | ${true}
+ ${'loading'} | ${'idle'} | ${false}
+ ${'loading'} | ${'errored'} | ${false}
+ ${'errored'} | ${'errored'} | ${true}
+ ${'errored'} | ${'idle'} | ${false}
+ ${'errored'} | ${'loading'} | ${false}
+ `(
+ 'returns the $result for "$request" when the state is "$state"',
+ ({ request, result, state }) => {
+ wrapper.vm.state = state;
+
+ expect(wrapper.vm.is(request)).toEqual(result);
+ },
+ );
+ });
+
+ describe('transition', () => {
+ beforeEach(() => {
+ wrapper = createRenamedComponent({ props: { diffFile } });
+ });
+
+ it.each`
+ state | transition | result
+ ${'idle'} | ${TRANSITION_LOAD_START} | ${STATE_LOADING}
+ ${'idle'} | ${TRANSITION_LOAD_ERROR} | ${STATE_IDLING}
+ ${'idle'} | ${TRANSITION_LOAD_SUCCEED} | ${STATE_IDLING}
+ ${'idle'} | ${TRANSITION_ACKNOWLEDGE_ERROR} | ${STATE_IDLING}
+ ${'loading'} | ${TRANSITION_LOAD_START} | ${STATE_LOADING}
+ ${'loading'} | ${TRANSITION_LOAD_ERROR} | ${STATE_ERRORED}
+ ${'loading'} | ${TRANSITION_LOAD_SUCCEED} | ${STATE_IDLING}
+ ${'loading'} | ${TRANSITION_ACKNOWLEDGE_ERROR} | ${STATE_LOADING}
+ ${'errored'} | ${TRANSITION_LOAD_START} | ${STATE_LOADING}
+ ${'errored'} | ${TRANSITION_LOAD_ERROR} | ${STATE_ERRORED}
+ ${'errored'} | ${TRANSITION_LOAD_SUCCEED} | ${STATE_ERRORED}
+ ${'errored'} | ${TRANSITION_ACKNOWLEDGE_ERROR} | ${STATE_IDLING}
+ `(
+ 'correctly updates the state to "$result" when it starts as "$state" and the transition is "$transition"',
+ ({ state, transition, result }) => {
+ wrapper.vm.state = state;
+
+ wrapper.vm.transition(transition);
+
+ expect(wrapper.vm.state).toEqual(result);
+ },
+ );
+ });
+
+ describe('switchToFull', () => {
+ let store;
+
+ beforeEach(() => {
+ store = new Vuex.Store({
+ modules: {
+ diffs: {
+ namespaced: true,
+ actions: { switchToFullDiffFromRenamedFile: () => {} },
+ },
+ },
+ });
+
+ jest.spyOn(store, 'dispatch');
+
+ wrapper = createRenamedComponent({ props: { diffFile }, store });
+ });
+
+ afterEach(() => {
+ store = null;
+ });
+
+ it('calls the switchToFullDiffFromRenamedFile action when the method is triggered', () => {
+ store.dispatch.mockResolvedValue();
+
+ wrapper.vm.switchToFull();
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('diffs/switchToFullDiffFromRenamedFile', {
+ diffFile,
+ });
+ });
+ });
+
+ it.each`
+ after | resolvePromise | resolution
+ ${STATE_IDLING} | ${'mockResolvedValue'} | ${'successful'}
+ ${STATE_ERRORED} | ${'mockRejectedValue'} | ${'rejected'}
+ `(
+ 'moves through the correct states during a $resolution request',
+ ({ after, resolvePromise }) => {
+ store.dispatch[resolvePromise]();
+
+ expect(wrapper.vm.state).toEqual(STATE_IDLING);
+
+ wrapper.vm.switchToFull();
+
+ expect(wrapper.vm.state).toEqual(STATE_LOADING);
+
+ return (
+ wrapper.vm
+ // This tick is needed for when the action (promise) finishes
+ .$nextTick()
+ // This tick waits for the state change in the promise .then/.catch to bubble into the component
+ .then(() => wrapper.vm.$nextTick())
+ .then(() => {
+ expect(wrapper.vm.state).toEqual(after);
+ })
+ );
+ },
+ );
+ });
+
+ describe('clickLink', () => {
+ let event;
+
+ beforeEach(() => {
+ event = {
+ preventDefault: jest.fn(),
+ };
+ });
+
+ it.each`
+ alternateViewer | stops | handled
+ ${'text'} | ${true} | ${'should'}
+ ${'nottext'} | ${false} | ${'should not'}
+ `(
+ 'given { alternate_viewer: { name: "$alternateViewer" } }, the click event $handled be handled in the component',
+ ({ alternateViewer, stops }) => {
+ wrapper = createRenamedComponent({
+ props: {
+ diffFile: {
+ ...diffFile,
+ alternate_viewer: { name: alternateViewer },
+ },
+ },
+ });
+
+ jest.spyOn(wrapper.vm, 'switchToFull').mockImplementation(() => {});
+
+ wrapper.vm.clickLink(event);
+
+ if (stops) {
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(wrapper.vm.switchToFull).toHaveBeenCalled();
+ } else {
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ expect(wrapper.vm.switchToFull).not.toHaveBeenCalled();
+ }
+ },
+ );
+ });
+
+ describe('dismissError', () => {
+ let transitionSpy;
+
+ beforeEach(() => {
+ wrapper = createRenamedComponent({ props: { diffFile } });
+ transitionSpy = jest.spyOn(wrapper.vm, 'transition');
+ });
+
+ it(`transitions the component with "${TRANSITION_ACKNOWLEDGE_ERROR}"`, () => {
+ wrapper.vm.dismissError();
+
+ expect(transitionSpy).toHaveBeenCalledWith(TRANSITION_ACKNOWLEDGE_ERROR);
+ });
+ });
+
+ describe('output', () => {
+ it.each`
+ altViewer | nameDisplay
+ ${'text'} | ${'"text"'}
+ ${'nottext'} | ${'"nottext"'}
+ ${undefined} | ${undefined}
+ ${null} | ${null}
+ `(
+ 'with { alternate_viewer: { name: $nameDisplay } }, renders the component',
+ ({ altViewer }) => {
+ const file = { ...diffFile };
+
+ file.alternate_viewer.name = altViewer;
+ wrapper = createRenamedComponent({ props: { diffFile: file } });
+
+ expect(wrapper.find('[test-id="plaintext"]').text()).toEqual(
+ 'File renamed with no changes.',
+ );
+ },
+ );
+
+ it.each`
+ altType | linkText
+ ${'text'} | ${'Show file contents'}
+ ${'nottext'} | ${`View file @ ${DIFF_FILE_SHORT_SHA}`}
+ `(
+ 'includes a link to the full file for alternate viewer type "$altType"',
+ ({ altType, linkText }) => {
+ const file = { ...diffFile };
+ const clickMock = jest.fn().mockImplementation(() => {});
+
+ file.alternate_viewer.name = altType;
+ wrapper = createRenamedComponent({
+ deep: true,
+ props: { diffFile: file },
+ methods: {
+ clickLink: clickMock,
+ },
+ });
+
+ const link = wrapper.find('a');
+
+ expect(link.text()).toEqual(linkText);
+ expect(link.attributes('href')).toEqual(DIFF_FILE_VIEW_PATH);
+
+ link.trigger('click');
+
+ expect(clickMock).toHaveBeenCalled();
+ },
+ );
+ });
+});
diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js
deleted file mode 100644
index a4e6b81acba..00000000000
--- a/spec/javascripts/ide/components/jobs/detail_spec.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import Vue from 'vue';
-import JobDetail from '~/ide/components/jobs/detail.vue';
-import { createStore } from '~/ide/stores';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
-import { jobs } from '../../mock_data';
-
-describe('IDE jobs detail view', () => {
- const Component = Vue.extend(JobDetail);
- let vm;
-
- beforeEach(() => {
- const store = createStore();
-
- store.state.pipelines.detailJob = {
- ...jobs[0],
- isLoading: true,
- output: 'testing',
- rawPath: `${gl.TEST_HOST}/raw`,
- };
-
- vm = createComponentWithStore(Component, store);
-
- spyOn(vm, 'fetchJobTrace').and.returnValue(Promise.resolve());
-
- vm = vm.$mount();
-
- spyOn(vm.$refs.buildTrace, 'scrollTo');
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('calls fetchJobTrace on mount', () => {
- expect(vm.fetchJobTrace).toHaveBeenCalled();
- });
-
- it('scrolls to bottom on mount', done => {
- setTimeout(() => {
- expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalled();
-
- done();
- });
- });
-
- it('renders job output', () => {
- expect(vm.$el.querySelector('.bash').textContent).toContain('testing');
- });
-
- it('renders empty message output', done => {
- vm.$store.state.pipelines.detailJob.output = '';
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.bash').textContent).toContain('No messages were logged');
-
- done();
- });
- });
-
- it('renders loading icon', () => {
- expect(vm.$el.querySelector('.build-loader-animation')).not.toBe(null);
- expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('');
- });
-
- it('hides output when loading', () => {
- expect(vm.$el.querySelector('.bash')).not.toBe(null);
- expect(vm.$el.querySelector('.bash').style.display).toBe('none');
- });
-
- it('hide loading icon when isLoading is false', done => {
- vm.$store.state.pipelines.detailJob.isLoading = false;
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('none');
-
- done();
- });
- });
-
- it('resets detailJob when clicking header button', () => {
- spyOn(vm, 'setDetailJob');
-
- vm.$el.querySelector('.btn').click();
-
- expect(vm.setDetailJob).toHaveBeenCalledWith(null);
- });
-
- it('renders raw path link', () => {
- expect(vm.$el.querySelector('.controllers-buttons').getAttribute('href')).toBe(
- `${gl.TEST_HOST}/raw`,
- );
- });
-
- describe('scroll buttons', () => {
- it('triggers scrollDown when clicking down button', done => {
- spyOn(vm, 'scrollDown');
-
- vm.$el.querySelectorAll('.btn-scroll')[1].click();
-
- vm.$nextTick(() => {
- expect(vm.scrollDown).toHaveBeenCalled();
-
- done();
- });
- });
-
- it('triggers scrollUp when clicking up button', done => {
- spyOn(vm, 'scrollUp');
-
- vm.scrollPos = 1;
-
- vm.$nextTick()
- .then(() => vm.$el.querySelector('.btn-scroll').click())
- .then(() => vm.$nextTick())
- .then(() => {
- expect(vm.scrollUp).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('scrollDown', () => {
- it('scrolls build trace to bottom', () => {
- spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(1000);
-
- vm.scrollDown();
-
- expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 1000);
- });
- });
-
- describe('scrollUp', () => {
- it('scrolls build trace to top', () => {
- vm.scrollUp();
-
- expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 0);
- });
- });
-
- describe('scrollBuildLog', () => {
- beforeEach(() => {
- spyOnProperty(vm.$refs.buildTrace, 'offsetHeight').and.returnValue(100);
- spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(200);
- });
-
- it('sets scrollPos to bottom when at the bottom', done => {
- spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(100);
-
- vm.scrollBuildLog();
-
- setTimeout(() => {
- expect(vm.scrollPos).toBe(1);
-
- done();
- });
- });
-
- it('sets scrollPos to top when at the top', done => {
- spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(0);
- vm.scrollPos = 1;
-
- vm.scrollBuildLog();
-
- setTimeout(() => {
- expect(vm.scrollPos).toBe(0);
-
- done();
- });
- });
-
- it('resets scrollPos when not at top or bottom', done => {
- spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(10);
-
- vm.scrollBuildLog();
-
- setTimeout(() => {
- expect(vm.scrollPos).toBe('');
-
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js
deleted file mode 100644
index 2c52780f316..00000000000
--- a/spec/javascripts/ide/helpers.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from '../../frontend/ide/helpers';
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
deleted file mode 100644
index 27f0ad01f54..00000000000
--- a/spec/javascripts/ide/mock_data.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from '../../frontend/ide/mock_data';
diff --git a/spec/javascripts/vue_shared/components/file_finder/index_spec.js b/spec/javascripts/vue_shared/components/file_finder/index_spec.js
index 7ded228d3ea..bb927b7d7f2 100644
--- a/spec/javascripts/vue_shared/components/file_finder/index_spec.js
+++ b/spec/javascripts/vue_shared/components/file_finder/index_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Mousetrap from 'mousetrap';
-import { file } from 'spec/ide/helpers';
+import { file } from 'jest/ide/helpers';
import timeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import FindFileComponent from '~/vue_shared/components/file_finder/index.vue';
import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb
index ce1952b503f..44db95afc57 100644
--- a/spec/models/project_services/emails_on_push_service_spec.rb
+++ b/spec/models/project_services/emails_on_push_service_spec.rb
@@ -21,23 +21,25 @@ describe EmailsOnPushService do
end
end
- context 'when properties is missing branches_to_be_notified' do
- subject { described_class.new(properties: {}) }
+ describe '.new' do
+ context 'when properties is missing branches_to_be_notified' do
+ subject { described_class.new(properties: {}) }
- it 'sets the default value to all' do
- expect(subject.branches_to_be_notified).to eq('all')
+ it 'sets the default value to all' do
+ expect(subject.branches_to_be_notified).to eq('all')
+ end
end
- end
- context 'when branches_to_be_notified is already set' do
- subject { described_class.new(properties: { branches_to_be_notified: 'protected' }) }
+ context 'when branches_to_be_notified is already set' do
+ subject { described_class.new(properties: { branches_to_be_notified: 'protected' }) }
- it 'does not overwrite it with the default value' do
- expect(subject.branches_to_be_notified).to eq('protected')
+ it 'does not overwrite it with the default value' do
+ expect(subject.branches_to_be_notified).to eq('protected')
+ end
end
end
- context 'project emails' do
+ describe '#execute' do
let(:push_data) { { object_kind: 'push' } }
let(:project) { create(:project, :repository) }
let(:service) { create(:emails_on_push_service, project: project) }