Commit 194b499a authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 43b4b3e2
# 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
# 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
# frozen_string_literal: true
module Projects
module Prometheus
module Alerts
class DestroyService < BaseService
def execute(alert)
alert.destroy
end
end
end
end
end
# 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
---
title: Fix OpenAPI file detector
merge_request: 27321
author: Roger Meier
type: fixed
...@@ -64,9 +64,12 @@ Parameters: ...@@ -64,9 +64,12 @@ Parameters:
NOTE: **Note:** NOTE: **Note:**
[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), [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 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 ```json
[ [
...@@ -538,9 +541,12 @@ Parameters: ...@@ -538,9 +541,12 @@ Parameters:
NOTE: **Note:** NOTE: **Note:**
[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), [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 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 ```json
{ {
......
...@@ -16,6 +16,8 @@ To request access to Chatops on GitLab.com: ...@@ -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. 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`. 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 ## See also
- [Chatops Usage](../ci/chatops/README.md) - [Chatops Usage](../ci/chatops/README.md)
......
...@@ -40,7 +40,7 @@ module Gitlab ...@@ -40,7 +40,7 @@ module Gitlab
yarn_lock: 'yarn.lock', yarn_lock: 'yarn.lock',
# OpenAPI Specification files # OpenAPI Specification files
openapi: %r{.*(openapi|swagger).*\.(yaml|yml|json)\z}i openapi: %r{([^\/]+)*(openapi|swagger)([^\/]+)*\.(yaml|yml|json)\z}i
}.freeze }.freeze
# Returns an Array of file types based on the given paths. # Returns an Array of file types based on the given paths.
......
...@@ -71,6 +71,7 @@ module Gitlab ...@@ -71,6 +71,7 @@ module Gitlab
import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do
relation_object.save! relation_object.save!
log_relation_creation(@importable, relation_key, relation_object)
end end
rescue => e rescue => e
import_failure_service.log_import_failure( import_failure_service.log_import_failure(
...@@ -218,6 +219,25 @@ module Gitlab ...@@ -218,6 +219,25 @@ module Gitlab
relation_reader.sort_ci_pipelines_by_id relation_reader.sort_ci_pipelines_by_id
end 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 end
end end
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);
});
});
...@@ -57,7 +57,7 @@ describe('DiffDiscussions', () => { ...@@ -57,7 +57,7 @@ describe('DiffDiscussions', () => {
it('dispatches toggleDiscussion when clicking collapse button', () => { it('dispatches toggleDiscussion when clicking collapse button', () => {
createComponent({ shouldCollapseDiscussions: true }); createComponent({ shouldCollapseDiscussions: true });
spyOn(wrapper.vm.$store, 'dispatch').and.stub(); jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation();
const diffNotesToggle = findDiffNotesToggle(); const diffNotesToggle = findDiffNotesToggle();
diffNotesToggle.trigger('click'); diffNotesToggle.trigger('click');
...@@ -74,7 +74,7 @@ describe('DiffDiscussions', () => { ...@@ -74,7 +74,7 @@ describe('DiffDiscussions', () => {
expect(diffNotesToggle.text().trim()).toBe('1'); expect(diffNotesToggle.text().trim()).toBe('1');
expect(diffNotesToggle.classes()).toEqual( expect(diffNotesToggle.classes()).toEqual(
jasmine.arrayContaining(['btn-transparent', 'badge', 'badge-pill']), expect.arrayContaining(['btn-transparent', 'badge', 'badge-pill']),
); );
}); });
......
import Vue from 'vue'; import Vue from 'vue';
import { cloneDeep } from 'lodash'; 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 { createStore } from '~/mr_notes/stores';
import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue'; import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
import { getPreviousLineIndex } from '~/diffs/store/utils'; import { getPreviousLineIndex } from '~/diffs/store/utils';
...@@ -69,7 +69,7 @@ describe('DiffExpansionCell', () => { ...@@ -69,7 +69,7 @@ describe('DiffExpansionCell', () => {
mockLine = getLine(mockFile, INLINE_DIFF_VIEW_TYPE, LINE_TO_USE); mockLine = getLine(mockFile, INLINE_DIFF_VIEW_TYPE, LINE_TO_USE);
store = createStore(); store = createStore();
store.state.diffs.diffFiles = [mockFile]; store.state.diffs.diffFiles = [mockFile];
spyOn(store, 'dispatch').and.returnValue(Promise.resolve()); jest.spyOn(store, 'dispatch').mockReturnValue(Promise.resolve());
}); });
const createComponent = (options = {}) => { const createComponent = (options = {}) => {
......
import Vue from 'vue'; import Vue from 'vue';
import { createStore } from 'ee_else_ce/mr_notes/stores'; 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 { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
import DiffFileComponent from '~/diffs/components/diff_file.vue'; import DiffFileComponent from '~/diffs/components/diff_file.vue';
import { diffViewerModes, diffViewerErrors } from '~/ide/constants'; import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
import diffFileMockDataReadable from '../mock_data/diff_file'; import diffFileMockDataReadable from '../mock_data/diff_file';
...@@ -16,7 +16,7 @@ describe('DiffFile', () => { ...@@ -16,7 +16,7 @@ describe('DiffFile', () => {
file: JSON.parse(JSON.stringify(diffFileMockDataReadable)), file: JSON.parse(JSON.stringify(diffFileMockDataReadable)),
canCurrentUserFork: false, canCurrentUserFork: false,
}).$mount(); }).$mount();
trackingSpy = mockTracking('_category_', vm.$el, spyOn); trackingSpy = mockTracking('_category_', vm.$el, jest.spyOn);
}); });
afterEach(() => { afterEach(() => {
...@@ -164,7 +164,7 @@ describe('DiffFile', () => { ...@@ -164,7 +164,7 @@ describe('DiffFile', () => {
}); });
it('should update store state', done => { it('should update store state', done => {
spyOn(vm.$store, 'dispatch'); jest.spyOn(vm.$store, 'dispatch').mockImplementation(() => {});
vm.isCollapsed = true; vm.isCollapsed = true;
...@@ -211,7 +211,7 @@ describe('DiffFile', () => { ...@@ -211,7 +211,7 @@ describe('DiffFile', () => {
describe('watch collapsed', () => { describe('watch collapsed', () => {
it('calls handleLoadCollapsedDiff if collapsed changed & file has no lines', done => { 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.highlighted_diff_lines = undefined;
vm.file.parallel_diff_lines = []; vm.file.parallel_diff_lines = [];
...@@ -237,7 +237,7 @@ describe('DiffFile', () => { ...@@ -237,7 +237,7 @@ describe('DiffFile', () => {
canCurrentUserFork: false, canCurrentUserFork: false,
}).$mount(); }).$mount();
spyOn(vm, 'handleLoadCollapsedDiff'); jest.spyOn(vm, 'handleLoadCollapsedDiff').mockImplementation(() => {});
vm.file.highlighted_diff_lines = undefined; vm.file.highlighted_diff_lines = undefined;
vm.file.parallel_diff_lines = []; vm.file.parallel_diff_lines = [];
......
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue'; 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 { createStore } from '~/mr_notes/stores';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
import { noteableDataMock } from '../../notes/mock_data'; import { noteableDataMock } from '../../notes/mock_data';
describe('DiffLineNoteForm', () => { describe('DiffLineNoteForm', () => {
let component; let wrapper;
let diffFile; let diffFile;
let diffLines; let diffLines;
const getDiffFileMock = () => Object.assign({}, diffFileMockData); const getDiffFileMock = () => Object.assign({}, diffFileMockData);
...@@ -14,58 +14,57 @@ describe('DiffLineNoteForm', () => { ...@@ -14,58 +14,57 @@ describe('DiffLineNoteForm', () => {
beforeEach(() => { beforeEach(() => {
diffFile = getDiffFileMock(); diffFile = getDiffFileMock();
diffLines = diffFile.highlighted_diff_lines; diffLines = diffFile.highlighted_diff_lines;
const store = createStore();
component = createComponentWithStore(Vue.extend(DiffLineNoteForm), createStore(), { store.state.notes.userData.id = 1;
diffFileHash: diffFile.file_hash, store.state.notes.noteableData = noteableDataMock;
diffLines,
line: diffLines[0], wrapper = shallowMount(DiffLineNoteForm, {
noteTargetLine: diffLines[0], store,
}); propsData: {
diffFileHash: diffFile.file_hash,
Object.defineProperties(component, { diffLines,
noteableData: { value: noteableDataMock }, line: diffLines[0],
isLoggedIn: { value: true }, noteTargetLine: diffLines[0],
},
}); });
component.$mount();
}); });
describe('methods', () => { describe('methods', () => {
describe('handleCancelCommentForm', () => { describe('handleCancelCommentForm', () => {
it('should ask for confirmation when shouldConfirm and isDirty passed as truthy', () => { 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(); expect(window.confirm).toHaveBeenCalled();
}); });
it('should ask for confirmation when one of the params false', () => { 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(); expect(window.confirm).not.toHaveBeenCalled();
component.handleCancelCommentForm(false, true); wrapper.vm.handleCancelCommentForm(false, true);
expect(window.confirm).not.toHaveBeenCalled(); expect(window.confirm).not.toHaveBeenCalled();
}); });
it('should call cancelCommentForm with lineCode', done => { it('should call cancelCommentForm with lineCode', done => {
spyOn(window, 'confirm'); jest.spyOn(window, 'confirm').mockImplementation(() => {});
spyOn(component, 'cancelCommentForm'); jest.spyOn(wrapper.vm, 'cancelCommentForm').mockImplementation(() => {});
spyOn(component, 'resetAutoSave'); jest.spyOn(wrapper.vm, 'resetAutoSave').mockImplementation(() => {});
component.handleCancelCommentForm(); wrapper.vm.handleCancelCommentForm();
expect(window.confirm).not.toHaveBeenCalled(); expect(window.confirm).not.toHaveBeenCalled();
component.$nextTick(() => { wrapper.vm.$nextTick(() => {
expect(component.cancelCommentForm).toHaveBeenCalledWith({ expect(wrapper.vm.cancelCommentForm).toHaveBeenCalledWith({
lineCode: diffLines[0].line_code, lineCode: diffLines[0].line_code,
fileHash: component.diffFileHash, fileHash: wrapper.vm.diffFileHash,
}); });
expect(component.resetAutoSave).toHaveBeenCalled(); expect(wrapper.vm.resetAutoSave).toHaveBeenCalled();
done(); done();
}); });
...@@ -74,17 +73,16 @@ describe('DiffLineNoteForm', () => { ...@@ -74,17 +73,16 @@ describe('DiffLineNoteForm', () => {
describe('saveNoteForm', () => { describe('saveNoteForm', () => {
it('should call saveNote action with proper params', done => { it('should call saveNote action with proper params', done => {
const saveDiffDiscussionSpy = spyOn(component, 'saveDiffDiscussion').and.returnValue( const saveDiffDiscussionSpy = jest
Promise.resolve(), .spyOn(wrapper.vm, 'saveDiffDiscussion')
); .mockReturnValue(Promise.resolve());
spyOnProperty(component, 'formData').and.returnValue('formData');
component wrapper.vm
.handleSaveNote('note body') .handleSaveNote('note body')
.then(() => { .then(() => {
expect(saveDiffDiscussionSpy).toHaveBeenCalledWith({ expect(saveDiffDiscussionSpy).toHaveBeenCalledWith({
note: 'note body', note: 'note body',
formData: 'formData', formData: wrapper.vm.formData,
}); });
}) })
.then(done) .then(done)
...@@ -97,18 +95,14 @@ describe('DiffLineNoteForm', () => { ...@@ -97,18 +95,14 @@ describe('DiffLineNoteForm', () => {
it('should init autosave', () => { it('should init autosave', () => {
const key = 'autosave/Note/Issue/98//DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1'; const key = 'autosave/Note/Issue/98//DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1';
expect(component.autosave).toBeDefined(); expect(wrapper.vm.autosave).toBeDefined();
expect(component.autosave.key).toEqual(key); expect(wrapper.vm.autosave.key).toEqual(key);
}); });
}); });
describe('template', () => { describe('template', () => {
it('should have note form', () => { it('should have note form', () => {
const { $el } = component; expect(wrapper.find(NoteForm).exists()).toBe(true);
expect($el.querySelector('.js-vue-textarea')).toBeDefined();
expect($el.querySelector('.js-vue-issue-save')).toBeDefined();
expect($el.querySelector('.js-vue-markdown-field')).toBeDefined();
}); });
}); });
}); });
import Vue from 'vue'; 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'; import FileRowStats from '~/diffs/components/file_row_stats.vue';
describe('Diff file row stats', () => { describe('Diff file row stats', () => {
......
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { createStore } from '~/mr_notes/stores'; import { createStore } from '~/mr_notes/stores';
import { imageDiffDiscussions } from '../mock_data/diff_discussions'; import { imageDiffDiscussions } from '../mock_data/diff_discussions';
import Icon from '~/vue_shared/components/icon.vue';
describe('Diffs image diff overlay component', () => { describe('Diffs image diff overlay component', () => {
const dimensions = { const dimensions = {
width: 100, width: 100,
height: 200, height: 200,
}; };
let Component; let wrapper;
let vm; let dispatch;
const getAllImageBadges = () => wrapper.findAll('.js-image-badge');
function createComponent(props = {}, extendStore = () => {}) { function createComponent(props = {}, extendStore = () => {}) {
const store = createStore(); const store = createStore();
extendStore(store); extendStore(store);
dispatch = jest.spyOn(store, 'dispatch').mockImplementation();
vm = createComponentWithStore(Component, store, {
discussions: [...imageDiffDiscussions], wrapper = shallowMount(ImageDiffOverlay, {
fileHash: 'ABC', store,
...props, propsData: {
discussions: [...imageDiffDiscussions],
fileHash: 'ABC',
...props,
},
methods: {
getImageDimensions: jest.fn().mockReturnValue(dimensions),
},
}); });
} }
beforeAll(() => {
Component = Vue.extend(ImageDiffOverlay);
});
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
wrapper = null;
}); });
it('renders comment badges', () => { it('renders comment badges', () => {
createComponent(); createComponent();
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
vm.$mount();
expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(2); expect(getAllImageBadges()).toHaveLength(2);
}); });
it('renders index of discussion in badge', () => { it('renders index of discussion in badge', () => {
createComponent(); createComponent();
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); const imageBadges = getAllImageBadges();
vm.$mount();
expect(
expect(vm.$el.querySelectorAll('.js-image-badge')[0].textContent.trim()).toBe('1'); imageBadges
expect(vm.$el.querySelectorAll('.js-image-badge')[1].textContent.trim()).toBe('2'); .at(0)
.text()
.trim(),
).toBe('1');
expect(
imageBadges
.at(1)
.text()
.trim(),
).toBe('2');
}); });
it('renders icon when showCommentIcon is true', () => { it('renders icon when showCommentIcon is true', () => {
createComponent({ showCommentIcon: true }); createComponent({ showCommentIcon: true });
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
vm.$mount();
expect(vm.$el.querySelector('.js-image-badge svg')).not.toBe(null); expect(wrapper.find(Icon).exists()).toBe(true);
}); });
it('sets badge comment positions', () => { it('sets badge comment positions', () => {
createComponent(); createComponent();
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); const imageBadges = getAllImageBadges();
vm.$mount();
expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.left).toBe('10px'); expect(imageBadges.at(0).attributes('style')).toBe('left: 10px; top: 10px;');
expect(vm.$el.querySelectorAll('.js-image-badge')[0].style.top).toBe('10px'); expect(imageBadges.at(1).attributes('style')).toBe('left: 5px; top: 5px;');
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', () => { it('renders single badge for discussion object', () => {
...@@ -75,22 +81,15 @@ describe('Diffs image diff overlay component', () => { ...@@ -75,22 +81,15 @@ describe('Diffs image diff overlay component', () => {
...imageDiffDiscussions[0], ...imageDiffDiscussions[0],
}, },
}); });
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
vm.$mount();
expect(vm.$el.querySelectorAll('.js-image-badge').length).toBe(1); expect(getAllImageBadges()).toHaveLength(1);
}); });
it('dispatches openDiffFileCommentForm when clicking overlay', () => { it('dispatches openDiffFileCommentForm when clicking overlay', () => {
createComponent({ canComment: true }); createComponent({ canComment: true });
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); wrapper.find('.js-add-image-diff-note-button').trigger('click', { offsetX: 0, offsetY: 0 });
vm.$mount();
spyOn(vm.$store, 'dispatch').and.stub();
vm.$el.querySelector('.js-add-image-diff-note-button').click(); expect(dispatch).toHaveBeenCalledWith('diffs/openDiffFileCommentForm', {
expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/openDiffFileCommentForm', {
fileHash: 'ABC', fileHash: 'ABC',
x: 0, x: 0,
y: 0, y: 0,
...@@ -100,28 +99,26 @@ describe('Diffs image diff overlay component', () => { ...@@ -100,28 +99,26 @@ describe('Diffs image diff overlay component', () => {
}); });
describe('toggle discussion', () => { describe('toggle discussion', () => {
const getImageBadge = () => wrapper.find('.js-image-badge');
it('disables buttons when shouldToggleDiscussion is false', () => { it('disables buttons when shouldToggleDiscussion is false', () => {
createComponent({ shouldToggleDiscussion: false }); createComponent({ shouldToggleDiscussion: false });
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
vm.$mount();
expect(vm.$el.querySelector('.js-image-badge').hasAttribute('disabled')).toBe(true); expect(getImageBadge().attributes('disabled')).toEqual('disabled');
}); });
it('dispatches toggleDiscussion when clicking image badge', () => { it('dispatches toggleDiscussion when clicking image badge', () => {
createComponent(); createComponent();
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions); getImageBadge().trigger('click');
vm.$mount();
spyOn(vm.$store, 'dispatch').and.stub(); expect(dispatch).toHaveBeenCalledWith('toggleDiscussion', {
discussionId: '1',
vm.$el.querySelector('.js-image-badge').click(); });
expect(vm.$store.dispatch).toHaveBeenCalledWith('toggleDiscussion', { discussionId: '1' });
}); });
}); });
describe('comment form', () => { describe('comment form', () => {
const getCommentIndicator = () => wrapper.find('.comment-indicator');
beforeEach(() => { beforeEach(() => {
createComponent({}, store => { createComponent({}, store => {
store.state.diffs.commentForms.push({ store.state.diffs.commentForms.push({
...@@ -130,17 +127,14 @@ describe('Diffs image diff overlay component', () => { ...@@ -130,17 +127,14 @@ describe('Diffs image diff overlay component', () => {
y: 10, y: 10,
}); });
}); });
spyOn(vm, 'getImageDimensions').and.returnValue(dimensions);
vm.$mount();
}); });
it('renders comment form badge', () => { it('renders comment form badge', () => {
expect(vm.$el.querySelector('.comment-indicator')).not.toBe(null); expect(getCommentIndicator().exists()).toBe(true);
}); });
it('sets comment form badge position', () => { it('sets comment form badge position', () => {
expect(vm.$el.querySelector('.comment-indicator').style.left).toBe('20px'); expect(getCommentIndicator().attributes('style')).toBe('left: 20px; top: 10px;');
expect(vm.$el.querySelector('.comment-indicator').style.top).toBe('10px');
}); });
}); });
}); });
import Vue from 'vue'; 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 { createStore } from '~/mr_notes/stores';
import InlineDiffExpansionRow from '~/diffs/components/inline_diff_expansion_row.vue'; import InlineDiffExpansionRow from '~/diffs/components/inline_diff_expansion_row.vue';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
......
import Vue from 'vue'; 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 { createStore } from '~/mr_notes/stores';
import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue'; import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
......
import Vue from 'vue'; import Vue from 'vue';
import '~/behaviors/markdown/render_gfm'; import '~/behaviors/markdown/render_gfm';
import { createStore } from 'ee_else_ce/mr_notes/stores'; 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 InlineDiffView from '~/diffs/components/inline_diff_view.vue';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions'; import discussionsMockData from '../mock_data/diff_discussions';
......
import Vue from 'vue'; 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 { createStore } from '~/mr_notes/stores';
import ParallelDiffExpansionRow from '~/diffs/components/parallel_diff_expansion_row.vue'; import ParallelDiffExpansionRow from '~/diffs/components/parallel_diff_expansion_row.vue';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
......
import Vue from 'vue'; 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 { createStore } from '~/mr_notes/stores';
import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue'; import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
......
import Vue from 'vue'; import Vue from 'vue';
import { createStore } from 'ee_else_ce/mr_notes/stores'; 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 ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
import * as constants from '~/diffs/constants'; import * as constants from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
......
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,
};
...@@ -3,6 +3,7 @@ import mutations from '~/diffs/store/mutations'; ...@@ -3,6 +3,7 @@ import mutations from '~/diffs/store/mutations';
import * as types from '~/diffs/store/mutation_types'; import * as types from '~/diffs/store/mutation_types';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file'; import diffFileMockData from '../mock_data/diff_file';
import * as utils from '~/diffs/store/utils';
describe('DiffsStoreMutations', () => { describe('DiffsStoreMutations', () => {
describe('SET_BASE_CONFIG', () => { describe('SET_BASE_CONFIG', () => {
...@@ -181,21 +182,21 @@ describe('DiffsStoreMutations', () => { ...@@ -181,21 +182,21 @@ describe('DiffsStoreMutations', () => {
const state = { diffFiles: [diffFile], diffViewType: 'viewType' }; const state = { diffFiles: [diffFile], diffViewType: 'viewType' };
const lines = [{ old_line: 1, new_line: 1 }]; const lines = [{ old_line: 1, new_line: 1 }];
const findDiffFileSpy = spyOnDependency(mutations, 'findDiffFile').and.returnValue(diffFile); jest.spyOn(utils, 'findDiffFile').mockImplementation(() => diffFile);
const removeMatchLineSpy = spyOnDependency(mutations, 'removeMatchLine'); jest.spyOn(utils, 'removeMatchLine').mockImplementation(() => null);
const lineRefSpy = spyOnDependency(mutations, 'addLineReferences').and.returnValue(lines); jest.spyOn(utils, 'addLineReferences').mockImplementation(() => lines);
const addContextLinesSpy = spyOnDependency(mutations, 'addContextLines'); jest.spyOn(utils, 'addContextLines').mockImplementation(() => null);
mutations[types.ADD_CONTEXT_LINES](state, options); mutations[types.ADD_CONTEXT_LINES](state, options);
expect(findDiffFileSpy).toHaveBeenCalledWith(state.diffFiles, options.fileHash); expect(utils.findDiffFile).toHaveBeenCalledWith(state.diffFiles, options.fileHash);
expect(removeMatchLineSpy).toHaveBeenCalledWith( expect(utils.removeMatchLine).toHaveBeenCalledWith(
diffFile, diffFile,
options.lineNumbers, options.lineNumbers,
options.params.bottom, options.params.bottom,
); );
expect(lineRefSpy).toHaveBeenCalledWith( expect(utils.addLineReferences).toHaveBeenCalledWith(
options.contextLines, options.contextLines,
options.lineNumbers, options.lineNumbers,
options.params.bottom, options.params.bottom,
...@@ -203,7 +204,7 @@ describe('DiffsStoreMutations', () => { ...@@ -203,7 +204,7 @@ describe('DiffsStoreMutations', () => {
options.nextLineNumbers, options.nextLineNumbers,
); );
expect(addContextLinesSpy).toHaveBeenCalledWith({ expect(utils.addContextLines).toHaveBeenCalledWith({
inlineLines: diffFile.highlighted_diff_lines, inlineLines: diffFile.highlighted_diff_lines,
parallelLines: diffFile.parallel_diff_lines, parallelLines: diffFile.parallel_diff_lines,
diffViewType: 'viewType', diffViewType: 'viewType',
......
...@@ -463,7 +463,7 @@ describe('DiffsStoreUtils', () => { ...@@ -463,7 +463,7 @@ describe('DiffsStoreUtils', () => {
expect(updatedFilesList).toEqual([ expect(updatedFilesList).toEqual([
mock, mock,
jasmine.objectContaining({ expect.objectContaining({
content_sha: 'ABC', content_sha: 'ABC',
file_hash: 'DEF', file_hash: 'DEF',
}), }),
......
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);
});
});
// 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'; export { default } from '../../frontend/diffs/create_diffs_store';
export default { // No new code should be added to this file. Instead, modify the
submodule: false, // file this one re-exports from. For more detail about why, see:
submodule_link: null, // https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349
blob: {
id: '9e10516ca50788acf18c518a231914a21e5f16f7', export { default } from '../../../frontend/diffs/mock_data/diff_file_unreadable';
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,
};
...@@ -96,14 +96,25 @@ describe Gitlab::FileDetector do ...@@ -96,14 +96,25 @@ describe Gitlab::FileDetector do
'swagger.yml', 'swagger.yaml', 'swagger.json', 'swagger.yml', 'swagger.yaml', 'swagger.json',
'gitlab_swagger.yml', 'openapi_gitlab.yml', 'gitlab_swagger.yml', 'openapi_gitlab.yml',
'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON', '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| openapi_types.each do |type_name|
expect(described_class.type_of(type_name)).to eq(:openapi) expect(described_class.type_of(type_name)).to eq(:openapi)
end 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 end
end end
...@@ -60,6 +60,38 @@ describe Gitlab::ImportExport::RelationTreeRestorer do ...@@ -60,6 +60,38 @@ describe Gitlab::ImportExport::RelationTreeRestorer do
end end
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 context 'when restoring a project' do
let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' }
let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') }
...@@ -71,6 +103,30 @@ describe Gitlab::ImportExport::RelationTreeRestorer do ...@@ -71,6 +103,30 @@ describe Gitlab::ImportExport::RelationTreeRestorer do
let(:relation_reader) { Gitlab::ImportExport::JSON::LegacyReader::File.new(path, reader.project_relation_names) } let(:relation_reader) { Gitlab::ImportExport::JSON::LegacyReader::File.new(path, reader.project_relation_names) }
it_behaves_like 'import project successfully' 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 end
include_examples 'logging of relations creation'
end end
end end
...@@ -15,6 +15,10 @@ describe Groups::ImportExport::ImportService do ...@@ -15,6 +15,10 @@ describe Groups::ImportExport::ImportService do
before do before do
ImportExportUpload.create(group: group, import_file: import_file) 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 end
context 'when user has correct permissions' do context 'when user has correct permissions' do
...@@ -29,13 +33,11 @@ describe Groups::ImportExport::ImportService do ...@@ -29,13 +33,11 @@ describe Groups::ImportExport::ImportService do
end end
it 'logs the import success' do it 'logs the import success' do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
expect(import_logger).to receive(:info).with( expect(import_logger).to receive(:info).with(
group_id: group.id, group_id: group.id,
group_name: group.name, group_name: group.name,
message: 'Group Import/Export: Import succeeded' message: 'Group Import/Export: Import succeeded'
) ).once
subject subject
end end
...@@ -45,8 +47,6 @@ describe Groups::ImportExport::ImportService do ...@@ -45,8 +47,6 @@ describe Groups::ImportExport::ImportService do
let(:user) { create(:user) } let(:user) { create(:user) }
it 'logs the error and raises an exception' do 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( expect(import_logger).to receive(:error).with(
group_id: group.id, group_id: group.id,
group_name: group.name, group_name: group.name,
...@@ -71,16 +71,12 @@ describe Groups::ImportExport::ImportService do ...@@ -71,16 +71,12 @@ describe Groups::ImportExport::ImportService do
context 'when there are errors with the import file' do context 'when there are errors with the import file' do
let(:import_file) { fixture_file_upload('spec/fixtures/symlink_export.tar.gz') } 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 it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with( expect(import_logger).to receive(:error).with(
group_id: group.id, group_id: group.id,
group_name: group.name, group_name: group.name,
message: a_string_including('Errors occurred') message: a_string_including('Errors occurred')
) ).once
expect { subject }.to raise_error(Gitlab::ImportExport::Error) expect { subject }.to raise_error(Gitlab::ImportExport::Error)
end end
......
# 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
# 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
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment