diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-20 15:09:17 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-20 15:09:17 +0000 |
commit | 194b499aa8e26df26ff70a1e1ce0396587bd5243 (patch) | |
tree | c873ac9c3096faf4a5da43d6670107461da2a7d7 | |
parent | 43b4b3e2d2ddebc0a89b94a8251c162ec5719780 (diff) | |
download | gitlab-ce-194b499aa8e26df26ff70a1e1ce0396587bd5243.tar.gz |
Add latest changes from gitlab-org/gitlab@master
38 files changed, 849 insertions, 1123 deletions
diff --git a/app/services/projects/prometheus/alerts/alert_params.rb b/app/services/projects/prometheus/alerts/alert_params.rb new file mode 100644 index 00000000000..1c39ed36b12 --- /dev/null +++ b/app/services/projects/prometheus/alerts/alert_params.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Projects + module Prometheus + module Alerts + module AlertParams + def alert_params + return params if params[:operator].blank? + + params.merge( + operator: PrometheusAlert.operator_to_enum(params[:operator]) + ) + end + end + end + end +end diff --git a/app/services/projects/prometheus/alerts/create_service.rb b/app/services/projects/prometheus/alerts/create_service.rb new file mode 100644 index 00000000000..dc0cacf49f3 --- /dev/null +++ b/app/services/projects/prometheus/alerts/create_service.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Projects + module Prometheus + module Alerts + class CreateService < BaseService + include AlertParams + + def execute + project.prometheus_alerts.create(alert_params) + end + end + end + end +end diff --git a/app/services/projects/prometheus/alerts/destroy_service.rb b/app/services/projects/prometheus/alerts/destroy_service.rb new file mode 100644 index 00000000000..14e88a2e356 --- /dev/null +++ b/app/services/projects/prometheus/alerts/destroy_service.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Projects + module Prometheus + module Alerts + class DestroyService < BaseService + def execute(alert) + alert.destroy + end + end + end + end +end diff --git a/app/services/projects/prometheus/alerts/update_service.rb b/app/services/projects/prometheus/alerts/update_service.rb new file mode 100644 index 00000000000..a0c8a5ccc2d --- /dev/null +++ b/app/services/projects/prometheus/alerts/update_service.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Projects + module Prometheus + module Alerts + class UpdateService < BaseService + include AlertParams + + def execute(alert) + alert.update(alert_params) + end + end + end + end +end diff --git a/changelogs/unreleased/fix-openapi-file-detector.yml b/changelogs/unreleased/fix-openapi-file-detector.yml new file mode 100644 index 00000000000..7793df62e77 --- /dev/null +++ b/changelogs/unreleased/fix-openapi-file-detector.yml @@ -0,0 +1,5 @@ +--- +title: Fix OpenAPI file detector +merge_request: 27321 +author: Roger Meier +type: fixed diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 3c31ebf067b..0b90b277d6f 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,9 +64,12 @@ Parameters: NOTE: **Note:** [Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), -the mergeability (`merge_status`) of each merge request will be checked +when `async_merge_request_check_mergeability` feature flag is enabled, the +mergeability (`merge_status`) of each merge request will be checked asynchronously when a request is made to this endpoint. Poll this API endpoint -to get updated status. +to get updated status. This affects the `has_conflicts` property as it is +dependent on the `merge_status`. It'll return `false` unless `merge_status` is +`cannot_be_merged`. ```json [ @@ -538,9 +541,12 @@ Parameters: NOTE: **Note:** [Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), -the mergeability (`merge_status`) of a merge request will be checked +when `async_merge_request_check_mergeability` feature flag is enabled, the +mergeability (`merge_status`) of a merge request will be checked asynchronously when a request is made to this endpoint. Poll this API endpoint -to get updated status. +to get updated status. This affects the `has_conflicts` property as it is +dependent on the `merge_status`. It'll return `false` unless `merge_status` is +`cannot_be_merged`. ```json { diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md index 90b9cca54ac..a8151471b87 100644 --- a/doc/development/chatops_on_gitlabcom.md +++ b/doc/development/chatops_on_gitlabcom.md @@ -16,6 +16,8 @@ To request access to Chatops on GitLab.com: 1. Log into <https://ops.gitlab.net/users/sign_in> **using the same username** as for GitLab.com (you may have to rename it). 1. Ask in the [#production](https://gitlab.slack.com/messages/production) channel to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`. +NOTE: **Note:** If you had to change your username for GitLab.com on the first step, make sure [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page) on [the team page](https://about.gitlab.com/team). + ## See also - [Chatops Usage](../ci/chatops/README.md) diff --git a/lib/gitlab/file_detector.rb b/lib/gitlab/file_detector.rb index 305fbeecce1..351d5096132 100644 --- a/lib/gitlab/file_detector.rb +++ b/lib/gitlab/file_detector.rb @@ -40,7 +40,7 @@ module Gitlab yarn_lock: 'yarn.lock', # OpenAPI Specification files - openapi: %r{.*(openapi|swagger).*\.(yaml|yml|json)\z}i + openapi: %r{([^\/]+)*(openapi|swagger)([^\/]+)*\.(yaml|yml|json)\z}i }.freeze # Returns an Array of file types based on the given paths. diff --git a/lib/gitlab/import_export/relation_tree_restorer.rb b/lib/gitlab/import_export/relation_tree_restorer.rb index 88cf346d8ec..1157e18c7f9 100644 --- a/lib/gitlab/import_export/relation_tree_restorer.rb +++ b/lib/gitlab/import_export/relation_tree_restorer.rb @@ -71,6 +71,7 @@ module Gitlab import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do relation_object.save! + log_relation_creation(@importable, relation_key, relation_object) end rescue => e import_failure_service.log_import_failure( @@ -218,6 +219,25 @@ module Gitlab relation_reader.sort_ci_pipelines_by_id end + + # Enable logging of each top-level relation creation when Importing + # into a Group if feature flag is enabled + def log_relation_creation(importable, relation_key, relation_object) + root_ancestor_group = importable.try(:root_ancestor) + + return unless root_ancestor_group + return unless root_ancestor_group.instance_of?(::Group) + return unless Feature.enabled?(:log_import_export_relation_creation, root_ancestor_group) + + @shared.logger.info( + importable_type: importable.class.to_s, + importable_id: importable.id, + relation_key: relation_key, + relation_id: relation_object.id, + author_id: relation_object.try(:author_id), + message: '[Project/Group Import] Created new object relation' + ) + end end end end diff --git a/spec/frontend/diffs/components/commit_widget_spec.js b/spec/frontend/diffs/components/commit_widget_spec.js new file mode 100644 index 00000000000..54e7596b726 --- /dev/null +++ b/spec/frontend/diffs/components/commit_widget_spec.js @@ -0,0 +1,19 @@ +import { shallowMount } from '@vue/test-utils'; +import CommitWidget from '~/diffs/components/commit_widget.vue'; +import CommitItem from '~/diffs/components/commit_item.vue'; + +describe('diffs/components/commit_widget', () => { + let wrapper; + + beforeEach(() => { + wrapper = shallowMount(CommitWidget, { + propsData: { commit: {} }, + }); + }); + + it('renders commit item', () => { + const commitElement = wrapper.find(CommitItem); + + expect(commitElement.exists()).toBe(true); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_discussions_spec.js b/spec/frontend/diffs/components/diff_discussions_spec.js index 1b924bb947d..ba5a4f96204 100644 --- a/spec/javascripts/diffs/components/diff_discussions_spec.js +++ b/spec/frontend/diffs/components/diff_discussions_spec.js @@ -57,7 +57,7 @@ describe('DiffDiscussions', () => { it('dispatches toggleDiscussion when clicking collapse button', () => { createComponent({ shouldCollapseDiscussions: true }); - spyOn(wrapper.vm.$store, 'dispatch').and.stub(); + jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation(); const diffNotesToggle = findDiffNotesToggle(); diffNotesToggle.trigger('click'); @@ -74,7 +74,7 @@ describe('DiffDiscussions', () => { expect(diffNotesToggle.text().trim()).toBe('1'); expect(diffNotesToggle.classes()).toEqual( - jasmine.arrayContaining(['btn-transparent', 'badge', 'badge-pill']), + expect.arrayContaining(['btn-transparent', 'badge', 'badge-pill']), ); }); diff --git a/spec/javascripts/diffs/components/diff_expansion_cell_spec.js b/spec/frontend/diffs/components/diff_expansion_cell_spec.js index 9a5048d9332..31c6a4d5b60 100644 --- a/spec/javascripts/diffs/components/diff_expansion_cell_spec.js +++ b/spec/frontend/diffs/components/diff_expansion_cell_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import { cloneDeep } from 'lodash'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { createStore } from '~/mr_notes/stores'; import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue'; import { getPreviousLineIndex } from '~/diffs/store/utils'; @@ -69,7 +69,7 @@ describe('DiffExpansionCell', () => { mockLine = getLine(mockFile, INLINE_DIFF_VIEW_TYPE, LINE_TO_USE); store = createStore(); store.state.diffs.diffFiles = [mockFile]; - spyOn(store, 'dispatch').and.returnValue(Promise.resolve()); + jest.spyOn(store, 'dispatch').mockReturnValue(Promise.resolve()); }); const createComponent = (options = {}) => { diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js index e2b64a5418e..d0ba71fce47 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/frontend/diffs/components/diff_file_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import { createStore } from 'ee_else_ce/mr_notes/stores'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { mockTracking, triggerEvent } from 'helpers/tracking_helper'; import DiffFileComponent from '~/diffs/components/diff_file.vue'; import { diffViewerModes, diffViewerErrors } from '~/ide/constants'; import diffFileMockDataReadable from '../mock_data/diff_file'; @@ -16,7 +16,7 @@ describe('DiffFile', () => { file: JSON.parse(JSON.stringify(diffFileMockDataReadable)), canCurrentUserFork: false, }).$mount(); - trackingSpy = mockTracking('_category_', vm.$el, spyOn); + trackingSpy = mockTracking('_category_', vm.$el, jest.spyOn); }); afterEach(() => { @@ -164,7 +164,7 @@ describe('DiffFile', () => { }); it('should update store state', done => { - spyOn(vm.$store, 'dispatch'); + jest.spyOn(vm.$store, 'dispatch').mockImplementation(() => {}); vm.isCollapsed = true; @@ -211,7 +211,7 @@ describe('DiffFile', () => { describe('watch collapsed', () => { it('calls handleLoadCollapsedDiff if collapsed changed & file has no lines', done => { - spyOn(vm, 'handleLoadCollapsedDiff'); + jest.spyOn(vm, 'handleLoadCollapsedDiff').mockImplementation(() => {}); vm.file.highlighted_diff_lines = undefined; vm.file.parallel_diff_lines = []; @@ -237,7 +237,7 @@ describe('DiffFile', () => { canCurrentUserFork: false, }).$mount(); - spyOn(vm, 'handleLoadCollapsedDiff'); + jest.spyOn(vm, 'handleLoadCollapsedDiff').mockImplementation(() => {}); vm.file.highlighted_diff_lines = undefined; vm.file.parallel_diff_lines = []; diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/frontend/diffs/components/diff_line_note_form_spec.js index 09263e5ce83..9b032d10fdc 100644 --- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js +++ b/spec/frontend/diffs/components/diff_line_note_form_spec.js @@ -1,12 +1,12 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { shallowMount } from '@vue/test-utils'; import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue'; +import NoteForm from '~/notes/components/note_form.vue'; import { createStore } from '~/mr_notes/stores'; import diffFileMockData from '../mock_data/diff_file'; import { noteableDataMock } from '../../notes/mock_data'; describe('DiffLineNoteForm', () => { - let component; + let wrapper; let diffFile; let diffLines; const getDiffFileMock = () => Object.assign({}, diffFileMockData); @@ -14,58 +14,57 @@ describe('DiffLineNoteForm', () => { beforeEach(() => { diffFile = getDiffFileMock(); diffLines = diffFile.highlighted_diff_lines; - - component = createComponentWithStore(Vue.extend(DiffLineNoteForm), createStore(), { - diffFileHash: diffFile.file_hash, - diffLines, - line: diffLines[0], - noteTargetLine: diffLines[0], - }); - - Object.defineProperties(component, { - noteableData: { value: noteableDataMock }, - isLoggedIn: { value: true }, + const store = createStore(); + store.state.notes.userData.id = 1; + store.state.notes.noteableData = noteableDataMock; + + wrapper = shallowMount(DiffLineNoteForm, { + store, + propsData: { + diffFileHash: diffFile.file_hash, + diffLines, + line: diffLines[0], + noteTargetLine: diffLines[0], + }, }); - - component.$mount(); }); describe('methods', () => { describe('handleCancelCommentForm', () => { it('should ask for confirmation when shouldConfirm and isDirty passed as truthy', () => { - spyOn(window, 'confirm').and.returnValue(false); + jest.spyOn(window, 'confirm').mockReturnValue(false); - component.handleCancelCommentForm(true, true); + wrapper.vm.handleCancelCommentForm(true, true); expect(window.confirm).toHaveBeenCalled(); }); it('should ask for confirmation when one of the params false', () => { - spyOn(window, 'confirm').and.returnValue(false); + jest.spyOn(window, 'confirm').mockReturnValue(false); - component.handleCancelCommentForm(true, false); + wrapper.vm.handleCancelCommentForm(true, false); expect(window.confirm).not.toHaveBeenCalled(); - component.handleCancelCommentForm(false, true); + wrapper.vm.handleCancelCommentForm(false, true); expect(window.confirm).not.toHaveBeenCalled(); }); it('should call cancelCommentForm with lineCode', done => { - spyOn(window, 'confirm'); - spyOn(component, 'cancelCommentForm'); - spyOn(component, 'resetAutoSave'); - component.handleCancelCommentForm(); + jest.spyOn(window, 'confirm').mockImplementation(() => {}); + jest.spyOn(wrapper.vm, 'cancelCommentForm').mockImplementation(() => {}); + jest.spyOn(wrapper.vm, 'resetAutoSave').mockImplementation(() => {}); + wrapper.vm.handleCancelCommentForm(); expect(window.confirm).not.toHaveBeenCalled(); - component.$nextTick(() => { - expect(component.cancelCommentForm).toHaveBeenCalledWith({ + wrapper.vm.$nextTick(() => { + expect(wrapper.vm.cancelCommentForm).toHaveBeenCalledWith({ lineCode: diffLines[0].line_code, - fileHash: component.diffFileHash, + fileHash: wrapper.vm.diffFileHash, }); - expect(component.resetAutoSave).toHaveBeenCalled(); + expect(wrapper.vm.resetAutoSave).toHaveBeenCalled(); done(); }); @@ -74,17 +73,16 @@ describe('DiffLineNoteForm', () => { describe('saveNoteForm', () => { it('should call saveNote action with proper params', done => { - const saveDiffDiscussionSpy = spyOn(component, 'saveDiffDiscussion').and.returnValue( - Promise.resolve(), - ); - spyOnProperty(component, 'formData').and.returnValue('formData'); + const saveDiffDiscussionSpy = jest + .spyOn(wrapper.vm, 'saveDiffDiscussion') + .mockReturnValue(Promise.resolve()); - component + wrapper.vm .handleSaveNote('note body') .then(() => { expect(saveDiffDiscussionSpy).toHaveBeenCalledWith({ note: 'note body', - formData: 'formData', + formData: wrapper.vm.formData, }); }) .then(done) @@ -97,18 +95,14 @@ describe('DiffLineNoteForm', () => { it('should init autosave', () => { const key = 'autosave/Note/Issue/98//DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1'; - expect(component.autosave).toBeDefined(); - expect(component.autosave.key).toEqual(key); + expect(wrapper.vm.autosave).toBeDefined(); + expect(wrapper.vm.autosave.key).toEqual(key); }); }); describe('template', () => { it('should have note form', () => { - const { $el } = component; - - expect($el.querySelector('.js-vue-textarea')).toBeDefined(); - expect($el.querySelector('.js-vue-issue-save')).toBeDefined(); - expect($el.querySelector('.js-vue-markdown-field')).toBeDefined(); + expect(wrapper.find(NoteForm).exists()).toBe(true); }); }); }); diff --git a/spec/javascripts/diffs/components/file_row_stats_spec.js b/spec/frontend/diffs/components/file_row_stats_spec.js index 59c5e592a59..34d85ba10b0 100644 --- a/spec/javascripts/diffs/components/file_row_stats_spec.js +++ b/spec/frontend/diffs/components/file_row_stats_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import mountComponent from 'helpers/vue_mount_component_helper'; import FileRowStats from '~/diffs/components/file_row_stats.vue'; describe('Diff file row stats', () => { diff --git a/spec/frontend/diffs/components/image_diff_overlay_spec.js b/spec/frontend/diffs/components/image_diff_overlay_spec.js new file mode 100644 index 00000000000..accf0a972d0 --- /dev/null +++ b/spec/frontend/diffs/components/image_diff_overlay_spec.js @@ -0,0 +1,140 @@ +import { shallowMount } from '@vue/test-utils'; +import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; +import { createStore } from '~/mr_notes/stores'; +import { imageDiffDiscussions } from '../mock_data/diff_discussions'; +import Icon from '~/vue_shared/components/icon.vue'; + +describe('Diffs image diff overlay component', () => { + const dimensions = { + width: 100, + height: 200, + }; + let wrapper; + let dispatch; + const getAllImageBadges = () => wrapper.findAll('.js-image-badge'); + + function createComponent(props = {}, extendStore = () => {}) { + const store = createStore(); + + extendStore(store); + dispatch = jest.spyOn(store, 'dispatch').mockImplementation(); + + wrapper = shallowMount(ImageDiffOverlay, { + store, + propsData: { + discussions: [...imageDiffDiscussions], + fileHash: 'ABC', + ...props, + }, + methods: { + getImageDimensions: jest.fn().mockReturnValue(dimensions), + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('renders comment badges', () => { + createComponent(); + + expect(getAllImageBadges()).toHaveLength(2); + }); + + it('renders index of discussion in badge', () => { + createComponent(); + const imageBadges = getAllImageBadges(); + + expect( + imageBadges + .at(0) + .text() + .trim(), + ).toBe('1'); + expect( + imageBadges + .at(1) + .text() + .trim(), + ).toBe('2'); + }); + + it('renders icon when showCommentIcon is true', () => { + createComponent({ showCommentIcon: true }); + + expect(wrapper.find(Icon).exists()).toBe(true); + }); + + it('sets badge comment positions', () => { + createComponent(); + const imageBadges = getAllImageBadges(); + + expect(imageBadges.at(0).attributes('style')).toBe('left: 10px; top: 10px;'); + expect(imageBadges.at(1).attributes('style')).toBe('left: 5px; top: 5px;'); + }); + + it('renders single badge for discussion object', () => { + createComponent({ + discussions: { + ...imageDiffDiscussions[0], + }, + }); + + expect(getAllImageBadges()).toHaveLength(1); + }); + + it('dispatches openDiffFileCommentForm when clicking overlay', () => { + createComponent({ canComment: true }); + wrapper.find('.js-add-image-diff-note-button').trigger('click', { offsetX: 0, offsetY: 0 }); + + expect(dispatch).toHaveBeenCalledWith('diffs/openDiffFileCommentForm', { + fileHash: 'ABC', + x: 0, + y: 0, + width: 100, + height: 200, + }); + }); + + describe('toggle discussion', () => { + const getImageBadge = () => wrapper.find('.js-image-badge'); + + it('disables buttons when shouldToggleDiscussion is false', () => { + createComponent({ shouldToggleDiscussion: false }); + + expect(getImageBadge().attributes('disabled')).toEqual('disabled'); + }); + + it('dispatches toggleDiscussion when clicking image badge', () => { + createComponent(); + getImageBadge().trigger('click'); + + expect(dispatch).toHaveBeenCalledWith('toggleDiscussion', { + discussionId: '1', + }); + }); + }); + + describe('comment form', () => { + const getCommentIndicator = () => wrapper.find('.comment-indicator'); + beforeEach(() => { + createComponent({}, store => { + store.state.diffs.commentForms.push({ + fileHash: 'ABC', + x: 20, + y: 10, + }); + }); + }); + + it('renders comment form badge', () => { + expect(getCommentIndicator().exists()).toBe(true); + }); + + it('sets comment form badge position', () => { + expect(getCommentIndicator().attributes('style')).toBe('left: 20px; top: 10px;'); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js b/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js index 852b8c4fbfc..f423c3b111e 100644 --- a/spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js +++ b/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { createStore } from '~/mr_notes/stores'; import InlineDiffExpansionRow from '~/diffs/components/inline_diff_expansion_row.vue'; import diffFileMockData from '../mock_data/diff_file'; diff --git a/spec/javascripts/diffs/components/inline_diff_table_row_spec.js b/spec/frontend/diffs/components/inline_diff_table_row_spec.js index 392893eb695..66349727b11 100644 --- a/spec/javascripts/diffs/components/inline_diff_table_row_spec.js +++ b/spec/frontend/diffs/components/inline_diff_table_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { createStore } from '~/mr_notes/stores'; import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue'; import diffFileMockData from '../mock_data/diff_file'; diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/frontend/diffs/components/inline_diff_view_spec.js index 76d88d4d9f0..a63c13fb271 100644 --- a/spec/javascripts/diffs/components/inline_diff_view_spec.js +++ b/spec/frontend/diffs/components/inline_diff_view_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import '~/behaviors/markdown/render_gfm'; import { createStore } from 'ee_else_ce/mr_notes/stores'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import InlineDiffView from '~/diffs/components/inline_diff_view.vue'; import diffFileMockData from '../mock_data/diff_file'; import discussionsMockData from '../mock_data/diff_discussions'; diff --git a/spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js b/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js index f6a5a1096f3..15b2a824697 100644 --- a/spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js +++ b/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { createStore } from '~/mr_notes/stores'; import ParallelDiffExpansionRow from '~/diffs/components/parallel_diff_expansion_row.vue'; import diffFileMockData from '../mock_data/diff_file'; diff --git a/spec/javascripts/diffs/components/parallel_diff_table_row_spec.js b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js index 4e69382ba03..6b92d448cf5 100644 --- a/spec/javascripts/diffs/components/parallel_diff_table_row_spec.js +++ b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { createStore } from '~/mr_notes/stores'; import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue'; import diffFileMockData from '../mock_data/diff_file'; diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/frontend/diffs/components/parallel_diff_view_spec.js index 7daca25719b..0eefbc7ec08 100644 --- a/spec/javascripts/diffs/components/parallel_diff_view_spec.js +++ b/spec/frontend/diffs/components/parallel_diff_view_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import { createStore } from 'ee_else_ce/mr_notes/stores'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue'; import * as constants from '~/diffs/constants'; import diffFileMockData from '../mock_data/diff_file'; diff --git a/spec/frontend/diffs/mock_data/diff_file_unreadable.js b/spec/frontend/diffs/mock_data/diff_file_unreadable.js new file mode 100644 index 00000000000..8c2df45988e --- /dev/null +++ b/spec/frontend/diffs/mock_data/diff_file_unreadable.js @@ -0,0 +1,244 @@ +export default { + submodule: false, + submodule_link: null, + blob: { + id: '9e10516ca50788acf18c518a231914a21e5f16f7', + path: 'CHANGELOG', + name: 'CHANGELOG', + mode: '100644', + readable_text: false, + icon: 'file-text-o', + }, + blob_path: 'CHANGELOG', + blob_name: 'CHANGELOG', + blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>', + file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', + file_path: 'CHANGELOG', + new_file: false, + deleted_file: false, + renamed_file: false, + old_path: 'CHANGELOG', + new_path: 'CHANGELOG', + mode_changed: false, + a_mode: '100644', + b_mode: '100644', + text: true, + viewer: { + name: 'text', + error: null, + collapsed: false, + }, + added_lines: 0, + removed_lines: 0, + diff_refs: { + base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', + start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', + head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + }, + content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + stored_externally: null, + external_storage: null, + old_path_html: 'CHANGELOG', + new_path_html: 'CHANGELOG', + edit_path: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG', + view_path: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG', + replaced_view_path: null, + collapsed: false, + renderIt: false, + too_large: false, + context_lines_path: + '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', + highlighted_diff_lines: [ + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + discussions: [], + text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + discussions: [], + text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + rich_text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + ], + parallel_diff_lines: [ + { + left: { + type: 'empty-cell', + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + discussions: [], + text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + meta_data: null, + }, + }, + { + left: { + type: 'empty-cell', + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + discussions: [], + text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + }, + { + left: { + line_Code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + right: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + }, + ], + discussions: [], + renderingLines: false, +}; diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js index 7363a213847..8a1c3e56e5a 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/frontend/diffs/store/actions_spec.js @@ -1,13 +1,13 @@ import MockAdapter from 'axios-mock-adapter'; import Cookies from 'js-cookie'; -import mockDiffFile from 'spec/diffs/mock_data/diff_file'; +import mockDiffFile from 'jest/diffs/mock_data/diff_file'; import { DIFF_VIEW_COOKIE_NAME, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE, DIFFS_PER_PAGE, } from '~/diffs/constants'; -import actions, { +import { setBaseConfig, fetchDiffFiles, fetchDiffFilesBatch, @@ -48,14 +48,28 @@ import eventHub from '~/notes/event_hub'; import * as types from '~/diffs/store/mutation_types'; import axios from '~/lib/utils/axios_utils'; import testAction from '../../helpers/vuex_action_helper'; +import * as utils from '~/diffs/store/utils'; +import * as commonUtils from '~/lib/utils/common_utils'; +import { useLocalStorageSpy } from 'helpers/local_storage_helper'; +import createFlash from '~/flash'; + +jest.mock('~/flash', () => jest.fn()); describe('DiffsStoreActions', () => { + useLocalStorageSpy(); + const originalMethods = { requestAnimationFrame: global.requestAnimationFrame, requestIdleCallback: global.requestIdleCallback, }; beforeEach(() => { + jest.spyOn(window.history, 'pushState'); + jest.spyOn(commonUtils, 'historyPushState'); + jest.spyOn(commonUtils, 'handleLocationHash').mockImplementation(() => null); + jest.spyOn(commonUtils, 'scrollToElement').mockImplementation(() => null); + jest.spyOn(utils, 'convertExpandLines').mockImplementation(() => null); + jest.spyOn(utils, 'idleCallback').mockImplementation(() => null); ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => { global[method] = cb => { cb(); @@ -67,6 +81,7 @@ describe('DiffsStoreActions', () => { ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => { global[method] = originalMethods[method]; }); + createFlash.mockClear(); }); describe('setBaseConfig', () => { @@ -349,13 +364,11 @@ describe('DiffsStoreActions', () => { }); it('should show flash on API error', done => { - const flashSpy = spyOnDependency(actions, 'createFlash'); - mock.onGet(endpointCoverage).reply(400); testAction(fetchCoverageFiles, {}, { endpointCoverage }, [], [], () => { - expect(flashSpy).toHaveBeenCalledTimes(1); - expect(flashSpy).toHaveBeenCalledWith(jasmine.stringMatching('Something went wrong')); + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong')); done(); }); }); @@ -566,10 +579,10 @@ describe('DiffsStoreActions', () => { [{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }], [], () => { - setTimeout(() => { + setImmediate(() => { expect(Cookies.get('diff_view')).toEqual(INLINE_DIFF_VIEW_TYPE); done(); - }, 0); + }); }, ); }); @@ -584,10 +597,10 @@ describe('DiffsStoreActions', () => { [{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }], [], () => { - setTimeout(() => { + setImmediate(() => { expect(Cookies.get(DIFF_VIEW_COOKIE_NAME)).toEqual(PARALLEL_DIFF_VIEW_TYPE); done(); - }, 0); + }); }, ); }); @@ -661,7 +674,7 @@ describe('DiffsStoreActions', () => { const file = { hash: 123, load_collapsed_diff_url: '/load/collapsed/diff/url' }; const data = { hash: 123, parallelDiffLines: [{ lineCode: 1 }] }; const mock = new MockAdapter(axios); - const commit = jasmine.createSpy('commit'); + const commit = jest.fn(); mock.onGet(file.loadCollapsedDiffUrl).reply(200, data); loadCollapsedDiff({ commit, getters: { commitId: null }, state }, file) @@ -680,7 +693,7 @@ describe('DiffsStoreActions', () => { commitId: null, }; - spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} })); + jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve({ data: {} })); loadCollapsedDiff({ commit() {}, getters, state }, file); @@ -695,7 +708,7 @@ describe('DiffsStoreActions', () => { commitId: '123', }; - spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} })); + jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve({ data: {} })); loadCollapsedDiff({ commit() {}, getters, state }, file); @@ -725,12 +738,12 @@ describe('DiffsStoreActions', () => { describe('toggleFileDiscussions', () => { it('should dispatch collapseDiscussion when all discussions are expanded', () => { const getters = { - getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]), - diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(true), - diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(false), + getDiffFileDiscussions: jest.fn(() => [{ id: 1 }]), + diffHasAllExpandedDiscussions: jest.fn(() => true), + diffHasAllCollapsedDiscussions: jest.fn(() => false), }; - const dispatch = jasmine.createSpy('dispatch'); + const dispatch = jest.fn(); toggleFileDiscussions({ getters, dispatch }); @@ -743,12 +756,12 @@ describe('DiffsStoreActions', () => { it('should dispatch expandDiscussion when all discussions are collapsed', () => { const getters = { - getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]), - diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false), - diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(true), + getDiffFileDiscussions: jest.fn(() => [{ id: 1 }]), + diffHasAllExpandedDiscussions: jest.fn(() => false), + diffHasAllCollapsedDiscussions: jest.fn(() => true), }; - const dispatch = jasmine.createSpy(); + const dispatch = jest.fn(); toggleFileDiscussions({ getters, dispatch }); @@ -761,12 +774,12 @@ describe('DiffsStoreActions', () => { it('should dispatch expandDiscussion when some discussions are collapsed and others are expanded for the collapsed discussion', () => { const getters = { - getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ expanded: false, id: 1 }]), - diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false), - diffHasAllCollapsedDiscussions: jasmine.createSpy().and.returnValue(false), + getDiffFileDiscussions: jest.fn(() => [{ expanded: false, id: 1 }]), + diffHasAllExpandedDiscussions: jest.fn(() => false), + diffHasAllCollapsedDiscussions: jest.fn(() => false), }; - const dispatch = jasmine.createSpy(); + const dispatch = jest.fn(); toggleFileDiscussions({ getters, dispatch }); @@ -786,28 +799,22 @@ describe('DiffsStoreActions', () => { it('should not call handleLocationHash when there is not hash', () => { window.location.hash = ''; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededInline({}, lineMock); - expect(handleLocationHashSpy).not.toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).not.toHaveBeenCalled(); }); it('should not call handleLocationHash when the hash does not match any line', () => { window.location.hash = 'XYZ_456'; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededInline({}, lineMock); - expect(handleLocationHashSpy).not.toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).not.toHaveBeenCalled(); }); it('should call handleLocationHash only when the hash matches a line', () => { window.location.hash = 'ABC_123'; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededInline( {}, { @@ -822,8 +829,8 @@ describe('DiffsStoreActions', () => { }, ); - expect(handleLocationHashSpy).toHaveBeenCalled(); - expect(handleLocationHashSpy).toHaveBeenCalledTimes(1); + expect(commonUtils.handleLocationHash).toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).toHaveBeenCalledTimes(1); }); }); @@ -838,28 +845,22 @@ describe('DiffsStoreActions', () => { it('should not call handleLocationHash when there is not hash', () => { window.location.hash = ''; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededParallel({}, lineMock); - expect(handleLocationHashSpy).not.toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).not.toHaveBeenCalled(); }); it('should not call handleLocationHash when the hash does not match any line', () => { window.location.hash = 'XYZ_456'; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededParallel({}, lineMock); - expect(handleLocationHashSpy).not.toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).not.toHaveBeenCalled(); }); it('should call handleLocationHash only when the hash matches a line', () => { window.location.hash = 'ABC_123'; - const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); - scrollToLineIfNeededParallel( {}, { @@ -880,8 +881,8 @@ describe('DiffsStoreActions', () => { }, ); - expect(handleLocationHashSpy).toHaveBeenCalled(); - expect(handleLocationHashSpy).toHaveBeenCalledTimes(1); + expect(commonUtils.handleLocationHash).toHaveBeenCalled(); + expect(commonUtils.handleLocationHash).toHaveBeenCalledTimes(1); }); }); @@ -898,7 +899,7 @@ describe('DiffsStoreActions', () => { id: commitId, }, }; - const dispatch = jasmine.createSpy('dispatch').and.callFake(name => { + const dispatch = jest.fn(name => { switch (name) { case 'saveNote': return Promise.resolve({ @@ -913,17 +914,16 @@ describe('DiffsStoreActions', () => { saveDiffDiscussion({ state, dispatch }, { note, formData }) .then(() => { - const { calls } = dispatch; - - expect(calls.count()).toBe(5); - expect(calls.argsFor(0)).toEqual(['saveNote', jasmine.any(Object), { root: true }]); - - const postData = calls.argsFor(0)[1]; + expect(dispatch).toHaveBeenCalledTimes(5); + expect(dispatch).toHaveBeenNthCalledWith(1, 'saveNote', expect.any(Object), { + root: true, + }); + const postData = dispatch.mock.calls[0][1]; expect(postData.data.note.commit_id).toBe(commitId); - expect(calls.argsFor(1)).toEqual(['updateDiscussion', 'test', { root: true }]); - expect(calls.argsFor(2)).toEqual(['assignDiscussionsToDiff', ['discussion']]); + expect(dispatch).toHaveBeenNthCalledWith(2, 'updateDiscussion', 'test', { root: true }); + expect(dispatch).toHaveBeenNthCalledWith(3, 'assignDiscussionsToDiff', ['discussion']); }) .then(done) .catch(done.fail); @@ -947,12 +947,7 @@ describe('DiffsStoreActions', () => { let commit; beforeEach(() => { - commit = jasmine.createSpy(); - jasmine.clock().install(); - }); - - afterEach(() => { - jasmine.clock().uninstall(); + commit = jest.fn(); }); it('updates location hash', () => { @@ -990,7 +985,7 @@ describe('DiffsStoreActions', () => { }); it('updates localStorage', () => { - spyOn(localStorage, 'setItem'); + jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); toggleShowTreeList({ commit() {}, state: { showTreeList: true } }); @@ -998,7 +993,7 @@ describe('DiffsStoreActions', () => { }); it('does not update localStorage', () => { - spyOn(localStorage, 'setItem'); + jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); toggleShowTreeList({ commit() {}, state: { showTreeList: true } }, false); @@ -1027,7 +1022,6 @@ describe('DiffsStoreActions', () => { }; let commit; let $emit; - let scrollToElement; const state = ({ collapsed, renderIt }) => ({ diffFiles: [ { @@ -1041,9 +1035,8 @@ describe('DiffsStoreActions', () => { }); beforeEach(() => { - commit = jasmine.createSpy('commit'); - scrollToElement = spyOnDependency(actions, 'scrollToElement').and.stub(); - $emit = spyOn(eventHub, '$emit'); + commit = jest.fn(); + $emit = jest.spyOn(eventHub, '$emit'); }); it('renders and expands file for the given discussion id', () => { @@ -1053,7 +1046,7 @@ describe('DiffsStoreActions', () => { expect(commit).toHaveBeenCalledWith('RENDER_FILE', localState.diffFiles[0]); expect($emit).toHaveBeenCalledTimes(1); - expect(scrollToElement).toHaveBeenCalledTimes(1); + expect(commonUtils.scrollToElement).toHaveBeenCalledTimes(1); }); it('jumps to discussion on already rendered and expanded file', () => { @@ -1063,7 +1056,7 @@ describe('DiffsStoreActions', () => { expect(commit).not.toHaveBeenCalled(); expect($emit).toHaveBeenCalledTimes(1); - expect(scrollToElement).not.toHaveBeenCalled(); + expect(commonUtils.scrollToElement).not.toHaveBeenCalled(); }); }); @@ -1080,8 +1073,6 @@ describe('DiffsStoreActions', () => { }); it('sets localStorage', () => { - spyOn(localStorage, 'setItem').and.stub(); - setRenderTreeList({ commit() {} }, true); expect(localStorage.setItem).toHaveBeenCalledWith('mr_diff_tree_list', true); @@ -1090,7 +1081,7 @@ describe('DiffsStoreActions', () => { describe('setShowWhitespace', () => { beforeEach(() => { - spyOn(eventHub, '$emit').and.stub(); + jest.spyOn(eventHub, '$emit').mockImplementation(); }); it('commits SET_SHOW_WHITESPACE', done => { @@ -1105,41 +1096,30 @@ describe('DiffsStoreActions', () => { }); it('sets localStorage', () => { - spyOn(localStorage, 'setItem').and.stub(); - setShowWhitespace({ commit() {} }, { showWhitespace: true }); expect(localStorage.setItem).toHaveBeenCalledWith('mr_show_whitespace', true); }); it('calls history pushState', () => { - spyOn(localStorage, 'setItem').and.stub(); - spyOn(window.history, 'pushState').and.stub(); - setShowWhitespace({ commit() {} }, { showWhitespace: true, pushState: true }); expect(window.history.pushState).toHaveBeenCalled(); }); it('calls history pushState with merged params', () => { - const originalPushState = window.history; - - originalPushState.pushState({}, '', '?test=1'); - - spyOn(localStorage, 'setItem').and.stub(); - spyOn(window.history, 'pushState').and.stub(); + window.history.pushState({}, '', '?test=1'); setShowWhitespace({ commit() {} }, { showWhitespace: true, pushState: true }); - expect(window.history.pushState.calls.mostRecent().args[2]).toMatch(/(.*)\?test=1&w=0/); + expect( + window.history.pushState.mock.calls[window.history.pushState.mock.calls.length - 1][2], + ).toMatch(/(.*)\?test=1&w=0/); - originalPushState.pushState({}, '', '?'); + window.history.pushState({}, '', '?'); }); it('emits eventHub event', () => { - spyOn(localStorage, 'setItem').and.stub(); - spyOn(window.history, 'pushState').and.stub(); - setShowWhitespace({ commit() {} }, { showWhitespace: true, pushState: true }); expect(eventHub.$emit).toHaveBeenCalledWith('refetchDiffData'); @@ -1284,13 +1264,13 @@ describe('DiffsStoreActions', () => { describe('setExpandedDiffLines', () => { beforeEach(() => { - spyOnDependency(actions, 'idleCallback').and.callFake(cb => { + utils.idleCallback.mockImplementation(cb => { cb({ timeRemaining: () => 50 }); }); }); it('commits SET_CURRENT_VIEW_DIFF_FILE_LINES when lines less than MAX_RENDERING_DIFF_LINES', done => { - spyOnDependency(actions, 'convertExpandLines').and.callFake(() => ['test']); + utils.convertExpandLines.mockImplementation(() => ['test']); testAction( setExpandedDiffLines, @@ -1313,7 +1293,7 @@ describe('DiffsStoreActions', () => { it('commits ADD_CURRENT_VIEW_DIFF_FILE_LINES when lines more than MAX_RENDERING_DIFF_LINES', done => { const lines = new Array(501).fill().map((_, i) => `line-${i}`); - spyOnDependency(actions, 'convertExpandLines').and.callFake(() => lines); + utils.convertExpandLines.mockReturnValue(lines); testAction( setExpandedDiffLines, @@ -1347,7 +1327,7 @@ describe('DiffsStoreActions', () => { const mock = new MockAdapter(axios); mock.onPost(state.dismissEndpoint).reply(200, {}); - spyOn(axios, 'post').and.callThrough(); + jest.spyOn(axios, 'post'); testAction( setSuggestPopoverDismissed, diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/frontend/diffs/store/getters_spec.js index ca47f51cb15..ca47f51cb15 100644 --- a/spec/javascripts/diffs/store/getters_spec.js +++ b/spec/frontend/diffs/store/getters_spec.js diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js index c36aff39aa9..f486a53fc4d 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/frontend/diffs/store/mutations_spec.js @@ -3,6 +3,7 @@ import mutations from '~/diffs/store/mutations'; import * as types from '~/diffs/store/mutation_types'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; import diffFileMockData from '../mock_data/diff_file'; +import * as utils from '~/diffs/store/utils'; describe('DiffsStoreMutations', () => { describe('SET_BASE_CONFIG', () => { @@ -181,21 +182,21 @@ describe('DiffsStoreMutations', () => { const state = { diffFiles: [diffFile], diffViewType: 'viewType' }; const lines = [{ old_line: 1, new_line: 1 }]; - const findDiffFileSpy = spyOnDependency(mutations, 'findDiffFile').and.returnValue(diffFile); - const removeMatchLineSpy = spyOnDependency(mutations, 'removeMatchLine'); - const lineRefSpy = spyOnDependency(mutations, 'addLineReferences').and.returnValue(lines); - const addContextLinesSpy = spyOnDependency(mutations, 'addContextLines'); + jest.spyOn(utils, 'findDiffFile').mockImplementation(() => diffFile); + jest.spyOn(utils, 'removeMatchLine').mockImplementation(() => null); + jest.spyOn(utils, 'addLineReferences').mockImplementation(() => lines); + jest.spyOn(utils, 'addContextLines').mockImplementation(() => null); mutations[types.ADD_CONTEXT_LINES](state, options); - expect(findDiffFileSpy).toHaveBeenCalledWith(state.diffFiles, options.fileHash); - expect(removeMatchLineSpy).toHaveBeenCalledWith( + expect(utils.findDiffFile).toHaveBeenCalledWith(state.diffFiles, options.fileHash); + expect(utils.removeMatchLine).toHaveBeenCalledWith( diffFile, options.lineNumbers, options.params.bottom, ); - expect(lineRefSpy).toHaveBeenCalledWith( + expect(utils.addLineReferences).toHaveBeenCalledWith( options.contextLines, options.lineNumbers, options.params.bottom, @@ -203,7 +204,7 @@ describe('DiffsStoreMutations', () => { options.nextLineNumbers, ); - expect(addContextLinesSpy).toHaveBeenCalledWith({ + expect(utils.addContextLines).toHaveBeenCalledWith({ inlineLines: diffFile.highlighted_diff_lines, parallelLines: diffFile.parallel_diff_lines, diffViewType: 'viewType', diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js index 223c4d7e40b..1adcdab272a 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/frontend/diffs/store/utils_spec.js @@ -463,7 +463,7 @@ describe('DiffsStoreUtils', () => { expect(updatedFilesList).toEqual([ mock, - jasmine.objectContaining({ + expect.objectContaining({ content_sha: 'ABC', file_hash: 'DEF', }), diff --git a/spec/javascripts/diffs/components/commit_widget_spec.js b/spec/javascripts/diffs/components/commit_widget_spec.js deleted file mode 100644 index 2b60bd232ed..00000000000 --- a/spec/javascripts/diffs/components/commit_widget_spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import CommitWidget from '~/diffs/components/commit_widget.vue'; -import getDiffWithCommit from '../mock_data/diff_with_commit'; - -describe('diffs/components/commit_widget', () => { - const Component = Vue.extend(CommitWidget); - const { commit } = getDiffWithCommit(); - - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - commit: getDiffWithCommit().commit, - }); - }); - - it('renders commit item', () => { - const commitElement = vm.$el.querySelector('li.commit'); - - expect(commitElement).not.toBeNull(); - expect(commitElement).toContainText(commit.short_id); - }); -}); diff --git a/spec/javascripts/diffs/components/image_diff_overlay_spec.js b/spec/javascripts/diffs/components/image_diff_overlay_spec.js deleted file mode 100644 index c2e5c6c34aa..00000000000 --- a/spec/javascripts/diffs/components/image_diff_overlay_spec.js +++ /dev/null @@ -1,146 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; -import { createStore } from '~/mr_notes/stores'; -import { imageDiffDiscussions } from '../mock_data/diff_discussions'; - -describe('Diffs image diff overlay component', () => { - const dimensions = { - width: 100, - height: 200, - }; - let Component; - let vm; - - function createComponent(props = {}, extendStore = () => {}) { - const store = createStore(); - - extendStore(store); - - vm = createComponentWithStore(Component, store, { - discussions: [...imageDiffDiscussions], - fileHash: 'ABC', - ...props, - }); - } - - beforeAll(() => { - Component = Vue.extend(ImageDiffOverlay); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders comment badges', () => { - createComponent(); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(2); - }); - - it('renders index of discussion in badge', () => { - createComponent(); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelectorAll('.js-image-badge')[0].textContent.trim()).toBe('1'); - expect(vm.$el.querySelectorAll('.js-image-badge')[1].textContent.trim()).toBe('2'); - }); - - it('renders icon when showCommentIcon is true', () => { - createComponent({ showCommentIcon: true }); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelector('.js-image-badge svg')).not.toBe(null); - }); - - it('sets badge comment positions', () => { - createComponent(); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.left).toBe('10px'); - expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.top).toBe('10px'); - - expect(vm.$el.querySelectorAll('.js-image-badge')[1].style.left).toBe('5px'); - expect(vm.$el.querySelectorAll('.js-image-badge')[1].style.top).toBe('5px'); - }); - - it('renders single badge for discussion object', () => { - createComponent({ - discussions: { - ...imageDiffDiscussions[0], - }, - }); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(1); - }); - - it('dispatches openDiffFileCommentForm when clicking overlay', () => { - createComponent({ canComment: true }); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - spyOn(vm.$store, 'dispatch').and.stub(); - - vm.$el.querySelector('.js-add-image-diff-note-button').click(); - - expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/openDiffFileCommentForm', { - fileHash: 'ABC', - x: 0, - y: 0, - width: 100, - height: 200, - }); - }); - - describe('toggle discussion', () => { - it('disables buttons when shouldToggleDiscussion is false', () => { - createComponent({ shouldToggleDiscussion: false }); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - expect(vm.$el.querySelector('.js-image-badge').hasAttribute('disabled')).toBe(true); - }); - - it('dispatches toggleDiscussion when clicking image badge', () => { - createComponent(); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - - spyOn(vm.$store, 'dispatch').and.stub(); - - vm.$el.querySelector('.js-image-badge').click(); - - expect(vm.$store.dispatch).toHaveBeenCalledWith('toggleDiscussion', { discussionId: '1' }); - }); - }); - - describe('comment form', () => { - beforeEach(() => { - createComponent({}, store => { - store.state.diffs.commentForms.push({ - fileHash: 'ABC', - x: 20, - y: 10, - }); - }); - spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); - vm.$mount(); - }); - - it('renders comment form badge', () => { - expect(vm.$el.querySelector('.comment-indicator')).not.toBe(null); - }); - - it('sets comment form badge position', () => { - expect(vm.$el.querySelector('.comment-indicator').style.left).toBe('20px'); - expect(vm.$el.querySelector('.comment-indicator').style.top).toBe('10px'); - }); - }); -}); diff --git a/spec/javascripts/diffs/create_diffs_store.js b/spec/javascripts/diffs/create_diffs_store.js index cfefd4238b8..9df057dd8b2 100644 --- a/spec/javascripts/diffs/create_diffs_store.js +++ b/spec/javascripts/diffs/create_diffs_store.js @@ -1 +1,5 @@ +// No new code should be added to this file. Instead, modify the +// file this one re-exports from. For more detail about why, see: +// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 + export { default } from '../../frontend/diffs/create_diffs_store'; diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js index dc25dd1647a..17586fddd0f 100644 --- a/spec/javascripts/diffs/mock_data/diff_discussions.js +++ b/spec/javascripts/diffs/mock_data/diff_discussions.js @@ -1,534 +1,5 @@ -export default { - id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - reply_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - position: { - old_line: null, - new_line: 2, - old_path: 'CHANGELOG', - new_path: 'CHANGELOG', - base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', - start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', - head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', - }, - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', - expanded: true, - notes: [ - { - id: '1749', - type: 'DiffNote', - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-04-03T21:06:21.521Z', - updated_at: '2018-04-08T08:50:41.762Z', - system: false, - noteable_id: 51, - noteable_type: 'MergeRequest', - noteable_iid: 20, - human_access: 'Owner', - note: 'comment 1', - note_html: '<p dir="auto">comment 1</p>', - last_edited_at: '2018-04-08T08:50:41.762Z', - last_edited_by: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - current_user: { - can_edit: true, - can_award_emoji: true, - }, - resolved: false, - resolvable: true, - resolved_by: null, - discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-test/notes/1749/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1749&user_id=1', - path: '/gitlab-org/gitlab-test/notes/1749', - noteable_note_url: - 'http://localhost:3000/gitlab-org/gitlab-test/-/merge_requests/20#note_1749', - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - }, - { - id: '1753', - type: 'DiffNote', - attachment: null, - author: { - id: 1, - name: 'Fatih Acet', - username: 'fatihacet', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/fatihacevt', - }, - created_at: '2018-04-08T08:49:35.804Z', - updated_at: '2018-04-08T08:50:45.915Z', - system: false, - noteable_id: 51, - noteable_type: 'MergeRequest', - noteable_iid: 20, - human_access: 'Owner', - note: 'comment 2 is really long one', - note_html: '<p dir="auto">comment 2 is really long one</p>', - last_edited_at: '2018-04-08T08:50:45.915Z', - last_edited_by: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - current_user: { - can_edit: true, - can_award_emoji: true, - }, - resolved: false, - resolvable: true, - resolved_by: null, - discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-test/notes/1753/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1753&user_id=1', - path: '/gitlab-org/gitlab-test/notes/1753', - noteable_note_url: - 'http://localhost:3000/gitlab-org/gitlab-test/-/merge_requests/20#note_1753', - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - }, - { - id: '1754', - type: 'DiffNote', - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-04-08T08:50:48.294Z', - updated_at: '2018-04-08T08:50:48.294Z', - system: false, - noteable_id: 51, - noteable_type: 'MergeRequest', - noteable_iid: 20, - human_access: 'Owner', - note: 'comment 3', - note_html: '<p dir="auto">comment 3</p>', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - resolved: false, - resolvable: true, - resolved_by: null, - discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-test/notes/1754/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1754&user_id=1', - path: '/gitlab-org/gitlab-test/notes/1754', - noteable_note_url: - 'http://localhost:3000/gitlab-org/gitlab-test/-/merge_requests/20#note_1754', - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - }, - { - id: '1755', - type: 'DiffNote', - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-04-08T08:50:50.911Z', - updated_at: '2018-04-08T08:50:50.911Z', - system: false, - noteable_id: 51, - noteable_type: 'MergeRequest', - noteable_iid: 20, - human_access: 'Owner', - note: 'comment 4', - note_html: '<p dir="auto">comment 4</p>', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - resolved: false, - resolvable: true, - resolved_by: null, - discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-test/notes/1755/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1755&user_id=1', - path: '/gitlab-org/gitlab-test/notes/1755', - noteable_note_url: - 'http://localhost:3000/gitlab-org/gitlab-test/-/merge_requests/20#note_1755', - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - }, - { - id: '1756', - type: 'DiffNote', - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-04-08T08:50:53.895Z', - updated_at: '2018-04-08T08:50:53.895Z', - system: false, - noteable_id: 51, - noteable_type: 'MergeRequest', - noteable_iid: 20, - human_access: 'Owner', - note: 'comment 5', - note_html: '<p dir="auto">comment 5</p>', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - resolved: false, - resolvable: true, - resolved_by: null, - discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-test/notes/1756/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1756&user_id=1', - path: '/gitlab-org/gitlab-test/notes/1756', - noteable_note_url: - 'http://localhost:3000/gitlab-org/gitlab-test/-/merge_requests/20#note_1756', - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - }, - ], - individual_note: false, - resolvable: true, - resolved: false, - resolve_path: - '/gitlab-org/gitlab-test/-/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', - resolve_with_issue_path: - '/gitlab-org/gitlab-test/-/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', - diff_file: { - submodule: false, - submodule_link: null, - blob: { - id: '9e10516ca50788acf18c518a231914a21e5f16f7', - path: 'CHANGELOG', - name: 'CHANGELOG', - mode: '100644', - readable_text: true, - icon: 'file-text-o', - }, - blob_path: 'CHANGELOG', - blob_name: 'CHANGELOG', - blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>', - file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', - file_path: 'CHANGELOG.rb', - new_file: false, - deleted_file: false, - renamed_file: false, - old_path: 'CHANGELOG', - new_path: 'CHANGELOG', - mode_changed: false, - a_mode: '100644', - b_mode: '100644', - text: true, - added_lines: 2, - removed_lines: 0, - diff_refs: { - base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', - start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', - head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', - }, - content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', - stored_externally: null, - external_storage: null, - old_path_html: 'CHANGELOG_OLD', - new_path_html: 'CHANGELOG', - is_fully_expanded: true, - context_lines_path: - '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', - highlighted_diff_lines: [ - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', - type: 'new', - old_line: null, - new_line: 1, - text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', - type: 'new', - old_line: null, - new_line: 2, - text: '<span id="LC2" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - ], - parallel_diff_lines: [ - { - left: null, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', - type: 'new', - old_line: null, - new_line: 1, - text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - meta_data: null, - }, - }, - { - left: null, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', - type: 'new', - old_line: null, - new_line: 2, - text: '<span id="LC2" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - right: { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - }, - ], - viewer: { - name: 'text', - error: null, - }, - }, - diff_discussion: true, - truncated_diff_lines: [ - { - text: 'line', - rich_text: - '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n', - can_receive_suggestion: true, - line_code: '6f209374f7e565f771b95720abf46024c41d1885_1_1', - type: 'new', - old_line: null, - new_line: 1, - meta_data: null, - }, - ], -}; +// No new code should be added to this file. Instead, modify the +// file this one re-exports from. For more detail about why, see: +// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 -export const imageDiffDiscussions = [ - { - id: '1', - position: { - x: 10, - y: 10, - width: 100, - height: 200, - }, - }, - { - id: '2', - position: { - x: 5, - y: 5, - width: 100, - height: 200, - }, - }, -]; +export { default } from '../../../frontend/diffs/mock_data/diff_discussions'; diff --git a/spec/javascripts/diffs/mock_data/diff_file_unreadable.js b/spec/javascripts/diffs/mock_data/diff_file_unreadable.js index 8c2df45988e..09a0dc61847 100644 --- a/spec/javascripts/diffs/mock_data/diff_file_unreadable.js +++ b/spec/javascripts/diffs/mock_data/diff_file_unreadable.js @@ -1,244 +1,5 @@ -export default { - submodule: false, - submodule_link: null, - blob: { - id: '9e10516ca50788acf18c518a231914a21e5f16f7', - path: 'CHANGELOG', - name: 'CHANGELOG', - mode: '100644', - readable_text: false, - icon: 'file-text-o', - }, - blob_path: 'CHANGELOG', - blob_name: 'CHANGELOG', - blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>', - file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', - file_path: 'CHANGELOG', - new_file: false, - deleted_file: false, - renamed_file: false, - old_path: 'CHANGELOG', - new_path: 'CHANGELOG', - mode_changed: false, - a_mode: '100644', - b_mode: '100644', - text: true, - viewer: { - name: 'text', - error: null, - collapsed: false, - }, - added_lines: 0, - removed_lines: 0, - diff_refs: { - base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', - start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', - head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', - }, - content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', - stored_externally: null, - external_storage: null, - old_path_html: 'CHANGELOG', - new_path_html: 'CHANGELOG', - edit_path: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG', - view_path: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG', - replaced_view_path: null, - collapsed: false, - renderIt: false, - too_large: false, - context_lines_path: - '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', - highlighted_diff_lines: [ - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', - type: 'new', - old_line: null, - new_line: 1, - discussions: [], - text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', - type: 'new', - old_line: null, - new_line: 2, - discussions: [], - text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', - rich_text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - discussions: [], - text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - discussions: [], - text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - discussions: [], - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - discussions: [], - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - ], - parallel_diff_lines: [ - { - left: { - type: 'empty-cell', - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', - type: 'new', - old_line: null, - new_line: 1, - discussions: [], - text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', - meta_data: null, - }, - }, - { - left: { - type: 'empty-cell', - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', - type: 'new', - old_line: null, - new_line: 2, - discussions: [], - text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - }, - { - left: { - line_Code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - discussions: [], - text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', - type: null, - old_line: 1, - new_line: 3, - discussions: [], - text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - discussions: [], - text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', - type: null, - old_line: 2, - new_line: 4, - discussions: [], - text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', - rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - discussions: [], - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - right: { - line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', - type: null, - old_line: 3, - new_line: 5, - discussions: [], - text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', - meta_data: null, - }, - }, - { - left: { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - discussions: [], - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - right: { - line_code: null, - type: 'match', - old_line: null, - new_line: null, - discussions: [], - text: '', - rich_text: '', - meta_data: { - old_pos: 3, - new_pos: 5, - }, - }, - }, - ], - discussions: [], - renderingLines: false, -}; +// No new code should be added to this file. Instead, modify the +// file this one re-exports from. For more detail about why, see: +// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 + +export { default } from '../../../frontend/diffs/mock_data/diff_file_unreadable'; diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb index 3972bd24e80..5bf70ef898a 100644 --- a/spec/lib/gitlab/file_detector_spec.rb +++ b/spec/lib/gitlab/file_detector_spec.rb @@ -96,14 +96,25 @@ describe Gitlab::FileDetector do 'swagger.yml', 'swagger.yaml', 'swagger.json', 'gitlab_swagger.yml', 'openapi_gitlab.yml', 'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON', - 'openapi.gitlab.yml', 'gitlab.openapi.yml' + 'openapi.gitlab.yml', 'gitlab.openapi.yml', + 'attention/openapi.yml', 'attention/swagger.yml', + 'attention/gitlab_swagger.yml', 'attention/openapi_gitlab.yml', + 'openapi/openapi.yml', 'openapi/swagger.yml', + 'openapi/my_openapi.yml', 'openapi/swagger_one.yml' ] openapi_types.each do |type_name| expect(described_class.type_of(type_name)).to eq(:openapi) end - expect(described_class.type_of('openapiyml')).to be_nil + openapi_bad_types = [ + 'openapiyml', + 'openapi/attention.yaml', 'swagger/attention.yaml' + ] + + openapi_bad_types.each do |type_name| + expect(described_class.type_of(type_name)).to be_nil + end end end end diff --git a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb index 578418998c0..e36144b1a30 100644 --- a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb @@ -60,6 +60,38 @@ describe Gitlab::ImportExport::RelationTreeRestorer do end end + shared_examples 'logging of relations creation' do + context 'when log_import_export_relation_creation feature flag is enabled' do + before do + stub_feature_flags(log_import_export_relation_creation: { enabled: true, thing: group }) + end + + it 'logs top-level relation creation' do + expect(relation_tree_restorer.shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .at_least(:once) + + subject + end + end + + context 'when log_import_export_relation_creation feature flag is disabled' do + before do + stub_feature_flags(log_import_export_relation_creation: { enabled: false, thing: group }) + end + + it 'does not log top-level relation creation' do + expect(relation_tree_restorer.shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .never + + subject + end + end + end + context 'when restoring a project' do let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } @@ -71,6 +103,30 @@ describe Gitlab::ImportExport::RelationTreeRestorer do let(:relation_reader) { Gitlab::ImportExport::JSON::LegacyReader::File.new(path, reader.project_relation_names) } it_behaves_like 'import project successfully' + + context 'logging of relations creation' do + let(:group) { create(:group) } + let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project', group: group) } + + include_examples 'logging of relations creation' + end + end + end + + context 'when restoring a group' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json' } + let(:group) { create(:group) } + let(:importable) { create(:group, parent: group) } + let(:object_builder) { Gitlab::ImportExport::Group::ObjectBuilder } + let(:relation_factory) { Gitlab::ImportExport::Group::RelationFactory } + let(:relation_reader) { Gitlab::ImportExport::JSON::LegacyReader::File.new(path, reader.group_relation_names) } + let(:reader) do + Gitlab::ImportExport::Reader.new( + shared: shared, + config: Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.group_config_file).to_h + ) end + + include_examples 'logging of relations creation' end end diff --git a/spec/services/groups/import_export/import_service_spec.rb b/spec/services/groups/import_export/import_service_spec.rb index 49c786ef67f..d95bba38b3e 100644 --- a/spec/services/groups/import_export/import_service_spec.rb +++ b/spec/services/groups/import_export/import_service_spec.rb @@ -15,6 +15,10 @@ describe Groups::ImportExport::ImportService do before do ImportExportUpload.create(group: group, import_file: import_file) + + allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger) + allow(import_logger).to receive(:error) + allow(import_logger).to receive(:info) end context 'when user has correct permissions' do @@ -29,13 +33,11 @@ describe Groups::ImportExport::ImportService do end it 'logs the import success' do - allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger) - expect(import_logger).to receive(:info).with( group_id: group.id, group_name: group.name, message: 'Group Import/Export: Import succeeded' - ) + ).once subject end @@ -45,8 +47,6 @@ describe Groups::ImportExport::ImportService do let(:user) { create(:user) } it 'logs the error and raises an exception' do - allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger) - expect(import_logger).to receive(:error).with( group_id: group.id, group_name: group.name, @@ -71,16 +71,12 @@ describe Groups::ImportExport::ImportService do context 'when there are errors with the import file' do let(:import_file) { fixture_file_upload('spec/fixtures/symlink_export.tar.gz') } - before do - allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger) - end - it 'logs the error and raises an exception' do expect(import_logger).to receive(:error).with( group_id: group.id, group_name: group.name, message: a_string_including('Errors occurred') - ) + ).once expect { subject }.to raise_error(Gitlab::ImportExport::Error) end diff --git a/spec/services/projects/prometheus/alerts/create_service_spec.rb b/spec/services/projects/prometheus/alerts/create_service_spec.rb new file mode 100644 index 00000000000..50c776df734 --- /dev/null +++ b/spec/services/projects/prometheus/alerts/create_service_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Prometheus::Alerts::CreateService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + let(:service) { described_class.new(project, user, params) } + + subject { service.execute } + + describe '#execute' do + context 'with params' do + let_it_be(:environment) { create(:environment, project: project) } + + let_it_be(:metric) do + create(:prometheus_metric, project: project) + end + + let(:params) do + { + environment_id: environment.id, + prometheus_metric_id: metric.id, + operator: '<', + threshold: 1.0 + } + end + + it 'creates an alert' do + expect(subject).to be_persisted + + expect(subject).to have_attributes( + project: project, + environment: environment, + prometheus_metric: metric, + operator: 'lt', + threshold: 1.0 + ) + end + end + + context 'without params' do + let(:params) { {} } + + it 'fails to create' do + expect(subject).to be_new_record + expect(subject).to be_invalid + end + end + end +end diff --git a/spec/services/projects/prometheus/alerts/destroy_service_spec.rb b/spec/services/projects/prometheus/alerts/destroy_service_spec.rb new file mode 100644 index 00000000000..7205ace8308 --- /dev/null +++ b/spec/services/projects/prometheus/alerts/destroy_service_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Prometheus::Alerts::DestroyService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:alert) { create(:prometheus_alert, project: project) } + + let(:service) { described_class.new(project, user, nil) } + + describe '#execute' do + subject { service.execute(alert) } + + it 'deletes the alert' do + expect(subject).to be_truthy + + expect(alert).to be_destroyed + end + end +end diff --git a/spec/services/projects/prometheus/alerts/update_service_spec.rb b/spec/services/projects/prometheus/alerts/update_service_spec.rb new file mode 100644 index 00000000000..8a99c2679f7 --- /dev/null +++ b/spec/services/projects/prometheus/alerts/update_service_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Prometheus::Alerts::UpdateService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:environment) { create(:environment, project: project) } + + let_it_be(:alert) do + create(:prometheus_alert, project: project, environment: environment) + end + + let(:service) { described_class.new(project, user, params) } + + let(:params) do + { + environment_id: alert.environment_id, + prometheus_metric_id: alert.prometheus_metric_id, + operator: '==', + threshold: 2.0 + } + end + + describe '#execute' do + subject { service.execute(alert) } + + context 'with valid params' do + it 'updates the alert' do + expect(subject).to be_truthy + + expect(alert.reload).to have_attributes( + operator: 'eq', + threshold: 2.0 + ) + end + end + + context 'with invalid params' do + let(:other_environment) { create(:environment) } + + before do + params[:environment_id] = other_environment.id + end + + it 'fails to update' do + expect(subject).to be_falsey + + expect(alert).to be_invalid + end + end + end +end |