Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-08 09:09:39 +00:00
parent 79ddf16358
commit 5bdbc604c8
39 changed files with 492 additions and 139 deletions

View File

@ -35,6 +35,7 @@ import {
UPDATE_NOTE_ERROR,
designDeletionError,
} from '../../utils/error_messages';
import { trackDesignDetailView } from '../../utils/tracking';
import { DESIGNS_ROUTE_NAME } from '../../router/constants';
export default {
@ -257,8 +258,21 @@ export default {
query: this.$route.query,
});
},
trackEvent() {
trackDesignDetailView(
'issue-design-collection',
this.$route.query.version || this.latestVersionId,
this.isLatestVersion,
);
},
},
beforeRouteEnter(to, from, next) {
next(vm => {
vm.trackEvent();
});
},
beforeRouteUpdate(to, from, next) {
this.trackEvent();
this.closeCommentForm();
next();
},

View File

@ -0,0 +1,22 @@
import Tracking from '~/tracking';
function assembleDesignPayload(payloadArr) {
return {
value: {
'internal-object-refrerer': payloadArr[0],
'version-number': payloadArr[1],
'current-version': payloadArr[2],
},
};
}
// Tracking Constants
const DESIGN_TRACKING_PAGE_NAME = 'projects:issues:design';
// eslint-disable-next-line import/prefer-default-export
export function trackDesignDetailView(refrerer = '', designVersion = 1, latestVersion = false) {
Tracking.event(DESIGN_TRACKING_PAGE_NAME, 'design_viewed', {
label: 'design_viewed',
...assembleDesignPayload([refrerer, designVersion, latestVersion]),
});
}

View File

@ -86,11 +86,13 @@ class Projects::IssuesController < Projects::ApplicationController
)
build_params = issue_params.merge(
merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of],
discussion_to_resolve: params[:discussion_to_resolve]
discussion_to_resolve: params[:discussion_to_resolve],
confidential: !!Gitlab::Utils.to_boolean(params[:issue][:confidential])
)
service = Issues::BuildService.new(project, current_user, build_params)
@issue = @noteable = service.execute
@merge_request_to_resolve_discussions_of = service.merge_request_to_resolve_discussions_of
@discussion_to_resolve = service.discussions_to_resolve.first if params[:discussion_to_resolve]

View File

@ -7,7 +7,7 @@ module Projects::AlertManagementHelper
'enable-alert-management-path' => edit_project_service_path(project, AlertsService),
'empty-alert-svg-path' => image_path('illustrations/alert-management-empty-state.svg'),
'user-can-enable-alert-management' => can?(current_user, :admin_project, project).to_s,
'alert-management-enabled' => Feature.enabled?(:alert_management_minimal, project).to_s
'alert-management-enabled' => (!!project.alerts_service_activated?).to_s
}
end

View File

@ -1436,20 +1436,12 @@ class Project < ApplicationRecord
# Expires various caches before a project is renamed.
def expire_caches_before_rename(old_path)
repo = Repository.new(old_path, self, shard: repository_storage)
wiki = Repository.new("#{old_path}.wiki", self, shard: repository_storage, repo_type: Gitlab::GlRepository::WIKI)
design = Repository.new("#{old_path}#{Gitlab::GlRepository::DESIGN.path_suffix}", self, shard: repository_storage, repo_type: Gitlab::GlRepository::DESIGN)
project_repo = Repository.new(old_path, self, shard: repository_storage)
wiki_repo = Repository.new("#{old_path}#{Gitlab::GlRepository::WIKI.path_suffix}", self, shard: repository_storage, repo_type: Gitlab::GlRepository::WIKI)
design_repo = Repository.new("#{old_path}#{Gitlab::GlRepository::DESIGN.path_suffix}", self, shard: repository_storage, repo_type: Gitlab::GlRepository::DESIGN)
if repo.exists?
repo.before_delete
end
if wiki.exists?
wiki.before_delete
end
if design.exists?
design.before_delete
[project_repo, wiki_repo, design_repo].each do |repo|
repo.before_delete if repo.exists?
end
end

View File

@ -7,6 +7,7 @@ module Projects
GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze
MARKDOWN_LINE_BREAK = " \n".freeze
INCIDENT_LABEL_NAME = IncidentManagement::CreateIssueService::INCIDENT_LABEL[:title].freeze
METRIC_TIME_WINDOW = 30.minutes
def full_title
[environment_name, alert_title].compact.join(': ')
@ -119,9 +120,63 @@ module Projects
Array(hosts.value).join(' ')
end
def metric_embed_for_alert; end
def metric_embed_for_alert
url = embed_url_for_gitlab_alert || embed_url_for_self_managed_alert
"\n[](#{url})" if url
end
def embed_url_for_gitlab_alert
return unless gitlab_alert
metrics_dashboard_project_prometheus_alert_url(
project,
gitlab_alert.prometheus_metric_id,
environment_id: environment.id,
**alert_embed_window_params(embed_time)
)
end
def embed_url_for_self_managed_alert
return unless environment && full_query && title
metrics_dashboard_project_environment_url(
project,
environment,
embed_json: dashboard_for_self_managed_alert.to_json,
**alert_embed_window_params(embed_time)
)
end
def embed_time
starts_at ? Time.rfc3339(starts_at) : Time.current
end
def alert_embed_window_params(time)
{
start: format_embed_timestamp(time - METRIC_TIME_WINDOW),
end: format_embed_timestamp(time + METRIC_TIME_WINDOW)
}
end
def format_embed_timestamp(time)
time.utc.strftime('%FT%TZ')
end
def dashboard_for_self_managed_alert
{
panel_groups: [{
panels: [{
type: 'line-graph',
title: title,
y_label: y_label,
metrics: [{
query_range: full_query
}]
}]
}]
}
end
end
end
end
Projects::Prometheus::AlertPresenter.prepend_if_ee('EE::Projects::Prometheus::AlertPresenter')

View File

@ -65,15 +65,19 @@ module Issues
private
def whitelisted_issue_params
base_params = [:title, :description, :confidential]
admin_params = [:milestone_id]
if can?(current_user, :admin_issue, project)
params.slice(:title, :description, :milestone_id)
params.slice(*(base_params + admin_params))
else
params.slice(:title, :description)
params.slice(*base_params)
end
end
def build_issue_params
issue_params_with_info_from_discussions.merge(whitelisted_issue_params)
{ author: current_user }.merge(issue_params_with_info_from_discussions)
.merge(whitelisted_issue_params)
end
end
end

View File

@ -0,0 +1,5 @@
---
title: Moves embedded metrics for Prometheus alerts to Core
merge_request: 31203
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Adds URL parameter for confidential new issue creation
merge_request: 30250
author:
type: added

View File

@ -0,0 +1,42 @@
---
# Verifies that badges are not lower case, which won't render properly.
#
# For a list of all options, see https://errata-ai.github.io/vale/styles/
extends: existence
message: 'Badge "%s" must be capitalized.'
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges
level: error
scope: raw
raw:
- '(\*\*\(Core\)\*\*|'
- '\*\*\(core\)\*\*|'
- '\*\*\(Starter\)\*\*|'
- '\*\*\(starter\)\*\*|'
- '\*\*\(Premium\)\*\*|'
- '\*\*\(premium\)\*\*|'
- '\*\*\(Ultimate\)\*\*|'
- '\*\*\(ultimate\)\*\*|'
- '\*\*\(Core Only\)\*\*|'
- '\*\*\(Core only\)\*\*|'
- '\*\*\(core only\)\*\*|'
- '\*\*\(Starter Only\)\*\*|'
- '\*\*\(Starter only\)\*\*|'
- '\*\*\(starter only\)\*\*|'
- '\*\*\(Premium Only\)\*\*|'
- '\*\*\(Premium only\)\*\*|'
- '\*\*\(premium only\)\*\*|'
- '\*\*\(Ultimate Only\)\*\*|'
- '\*\*\(Ultimate only\)\*\*|'
- '\*\*\(ultimate only\)\*\*|'
- '\*\*\(Free Only\)\*\*|'
- '\*\*\(Free only\)\*\*|'
- '\*\*\(free only\)\*\*|'
- '\*\*\(Bronze Only\)\*\*|'
- '\*\*\(Bronze only\)\*\*|'
- '\*\*\(bronze only\)\*\*|'
- '\*\*\(Silver Only\)\*\*|'
- '\*\*\(Silver only\)\*\*|'
- '\*\*\(silver only\)\*\*|'
- '\*\*\(Gold Only\)\*\*|'
- '\*\*\(Gold only\)\*\*|'
- '\*\*\(gold only\)\*\*)'

View File

@ -140,7 +140,7 @@ The only changes to the site should be from the DAST scanner. Be aware that any
changes that users, scheduled tasks, database changes, code changes, other pipelines, or other scanners make to
the site during a scan could lead to inaccurate results.
### Authenticated scan
### Authentication
It's also possible to authenticate the user before performing the DAST checks:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -58,7 +58,7 @@ An epic's page contains the following tabs:
- Hover over the total counts to see a breakdown of open and closed items.
- **Roadmap**: a roadmap view of child epics which have start and due dates.
![epic view](img/epic_view_v12.3.png)
![epic view](img/epic_view_v13.0.png)
## Adding an issue to an epic
@ -75,6 +75,7 @@ the issue is automatically unlinked from its current parent.
To add an issue to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an issue**.
1. Identify the issue to be added, using either of the following methods:
- Paste the link of the issue.
@ -91,7 +92,7 @@ Creating an issue from an epic enables you to maintain focus on the broader cont
To create an issue from an epic:
1. On the epic's page, under **Epics and Issues**, click the arrow next to **Add an issue** and select **Create new issue**.
1. On the epic's page, under **Epics and Issues**, click the **Add** dropdown button and select **Create new issue**.
1. Under **Title**, enter the title for the new issue.
1. From the **Project** dropdown, select the project in which the issue should be created.
1. Click **Create issue**.
@ -128,6 +129,7 @@ the maximum depth being 5.
To add a child epic to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an epic**.
1. Identify the epic to be added, using either of the following methods:
- Paste the link of the epic.

View File

@ -92,9 +92,17 @@ field values using query string parameters in a URL. This is useful for embeddin
a URL in an external HTML page, and also certain scenarios where you want the user to
create an issue with certain fields prefilled.
The title, description, and description template fields can be prefilled using
this method. You cannot pre-fill both the description and description template fields
in the same URL (since a description template also populates the description field).
The title, description, description template, and confidential fields can be prefilled
using this method. You cannot pre-fill both the description and description template
fields in the same URL (since a description template also populates the description
field).
| Field | URL Parameter Name | Notes |
|----------------------|-----------------------|-------------------------------------------------------|
| title | `issue[title]` | |
| description | `issue[description]` | |
| description template | `issuable_template` | |
| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential |
Follow these examples to form your new issue URL with prefilled fields.
@ -102,6 +110,8 @@ Follow these examples to form your new issue URL with prefilled fields.
and a pre-filled description, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
- For a new issue in the GitLab Community Edition project with a pre-filled title
and a pre-filled description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
- For a new issue in the GitLab Community Edition project with a pre-filled title,
a pre-filled description, and the confidential flag set, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true`
## Moving Issues

View File

@ -163,7 +163,7 @@ module Gitlab
end
def parse_json(response_body)
Gitlab::Json.parse(response_body)
Gitlab::Json.parse(response_body, legacy_mode: true)
rescue JSON::ParserError
raise PrometheusClient::Error, 'Parsing response failed'
end

View File

@ -49,7 +49,7 @@ module Mattermost
end
def json_response(response)
json_response = Gitlab::Json.parse(response.body)
json_response = Gitlab::Json.parse(response.body, legacy_mode: true)
unless response.success?
raise Mattermost::ClientError.new(json_response['message'] || 'Undefined error')

View File

@ -1191,6 +1191,9 @@ msgstr ""
msgid "Add a link"
msgstr ""
msgid "Add a new issue"
msgstr ""
msgid "Add a numbered list"
msgstr ""
@ -1206,7 +1209,7 @@ msgstr ""
msgid "Add an SSH key"
msgstr ""
msgid "Add an existing issue to the epic."
msgid "Add an existing issue"
msgstr ""
msgid "Add an issue"
@ -6153,9 +6156,6 @@ msgstr ""
msgid "Create a new issue"
msgstr ""
msgid "Create a new issue and add it to the epic."
msgstr ""
msgid "Create a new repository"
msgstr ""
@ -6165,9 +6165,6 @@ msgstr ""
msgid "Create an account using:"
msgstr ""
msgid "Create an issue"
msgstr ""
msgid "Create an issue. Issues are created for each alert triggered."
msgstr ""
@ -8324,10 +8321,10 @@ msgstr ""
msgid "Epics, Issues, and Merge Requests"
msgstr ""
msgid "Epics|Add an epic"
msgid "Epics|Add a new epic"
msgstr ""
msgid "Epics|Add an existing epic as a child epic."
msgid "Epics|Add an existing epic"
msgstr ""
msgid "Epics|An error occurred while saving the %{epicDateType} date"
@ -8339,12 +8336,6 @@ msgstr ""
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
msgid "Epics|Create an epic within this group and add it as a child epic."
msgstr ""
msgid "Epics|Create new epic"
msgstr ""
msgid "Epics|How can I solve this?"
msgstr ""

View File

@ -73,6 +73,12 @@ function rspec_paralellized_job() {
export KNAPSACK_LOG_LEVEL="debug"
export KNAPSACK_REPORT_PATH="knapsack/${report_name}_report.json"
# There's a bug where artifacts are sometimes not downloaded. Since specs can run without the Knapsack report, we can
# handle the missing artifact gracefully here. See https://gitlab.com/gitlab-org/gitlab/-/issues/212349.
if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
fi
cp "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "${KNAPSACK_REPORT_PATH}"
if [[ -z "${KNAPSACK_TEST_FILE_PATTERN}" ]]; then

View File

@ -187,6 +187,33 @@ describe Projects::IssuesController do
expect(assigns(:issue)).to be_a_new(Issue)
end
where(:conf_value, :conf_result) do
[
[true, true],
['true', true],
['TRUE', true],
[false, false],
['false', false],
['FALSE', false]
]
end
with_them do
it 'sets the confidential flag to the expected value' do
get :new, params: {
namespace_id: project.namespace,
project_id: project,
issue: {
confidential: conf_value
}
}
assigned_issue = assigns(:issue)
expect(assigned_issue).to be_a_new(Issue)
expect(assigned_issue.confidential).to eq conf_result
end
end
it 'fills in an issue for a merge request' do
project_with_repository = create(:project, :repository)
project_with_repository.add_developer(user)

View File

@ -1,4 +1,5 @@
import { mount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueRouter from 'vue-router';
import App from '~/design_management/components/app.vue';
import Designs from '~/design_management/pages/index.vue';
@ -11,74 +12,66 @@ import {
} from '~/design_management/router/constants';
import '~/commons/bootstrap';
function factory(routeArg) {
const localVue = createLocalVue();
localVue.use(VueRouter);
window.gon = { sprite_icons: '' };
const router = createRouter('/');
if (routeArg !== undefined) {
router.push(routeArg);
}
return mount(App, {
localVue,
router,
mocks: {
$apollo: {
queries: {
designs: { loading: true },
design: { loading: true },
permissions: { loading: true },
},
},
},
});
}
jest.mock('mousetrap', () => ({
bind: jest.fn(),
unbind: jest.fn(),
}));
describe('Design management router', () => {
let vm;
let router;
function factory() {
const localVue = createLocalVue();
localVue.use(VueRouter);
window.gon = { sprite_icons: '' };
router = createRouter('/');
vm = mount(App, {
localVue,
router,
mocks: {
$apollo: {
queries: {
designs: { loading: true },
design: { loading: true },
permissions: { loading: true },
},
},
},
});
}
beforeEach(() => {
factory();
});
afterEach(() => {
vm.destroy();
router.app.$destroy();
window.location.hash = '';
});
describe.each([['/'], [{ name: ROOT_ROUTE_NAME }]])('root route', pushArg => {
describe.each([['/'], [{ name: ROOT_ROUTE_NAME }]])('root route', routeArg => {
it('pushes home component', () => {
router.push(pushArg);
const wrapper = factory(routeArg);
expect(vm.find(Designs).exists()).toBe(true);
expect(wrapper.find(Designs).exists()).toBe(true);
});
});
describe.each([['/designs'], [{ name: DESIGNS_ROUTE_NAME }]])('designs route', pushArg => {
describe.each([['/designs'], [{ name: DESIGNS_ROUTE_NAME }]])('designs route', routeArg => {
it('pushes designs root component', () => {
router.push(pushArg);
const wrapper = factory(routeArg);
expect(vm.find(Designs).exists()).toBe(true);
expect(wrapper.find(Designs).exists()).toBe(true);
});
});
describe.each([['/designs/1'], [{ name: DESIGN_ROUTE_NAME, params: { id: '1' } }]])(
'designs detail route',
pushArg => {
routeArg => {
it('pushes designs detail component', () => {
router.push(pushArg);
const wrapper = factory(routeArg);
return vm.vm.$nextTick().then(() => {
const detail = vm.find(DesignDetail);
return nextTick().then(() => {
const detail = wrapper.find(DesignDetail);
expect(detail.exists()).toBe(true);
expect(detail.props('id')).toEqual('1');
});

View File

@ -0,0 +1,51 @@
import { mockTracking } from 'helpers/tracking_helper';
import { trackDesignDetailView } from '~/design_management/utils/tracking';
function getTrackingSpy(key) {
return mockTracking(key, undefined, jest.spyOn);
}
describe('Tracking Events', () => {
describe('trackDesignDetailView', () => {
const eventKey = 'projects:issues:design';
const eventName = 'design_viewed';
it('trackDesignDetailView fires a tracking event when called', () => {
const trackingSpy = getTrackingSpy(eventKey);
trackDesignDetailView();
expect(trackingSpy).toHaveBeenCalledWith(
eventKey,
eventName,
expect.objectContaining({
label: eventName,
value: {
'internal-object-refrerer': '',
'version-number': 1,
'current-version': false,
},
}),
);
});
it('trackDesignDetailView allows to customize the value payload', () => {
const trackingSpy = getTrackingSpy(eventKey);
trackDesignDetailView('from-a-test', 100, true);
expect(trackingSpy).toHaveBeenCalledWith(
eventKey,
eventName,
expect.objectContaining({
label: eventName,
value: {
'internal-object-refrerer': 'from-a-test',
'version-number': 100,
'current-version': true,
},
}),
);
});
});
});

View File

@ -10,7 +10,7 @@ import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import NoteForm from '~/notes/components/note_form.vue';
import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import diffFileMockData from '../../../javascripts/diffs/mock_data/diff_file';
import diffFileMockData from '../mock_data/diff_file';
import { diffViewerModes } from '~/ide/constants';
const localVue = createLocalVue();

View File

@ -25,7 +25,7 @@ describe('Code component', () => {
beforeEach(done => {
vm = setupComponent(json.cells[0]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -39,7 +39,7 @@ describe('Code component', () => {
beforeEach(done => {
vm = setupComponent(json.cells[2]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -60,7 +60,7 @@ describe('Code component', () => {
vm = setupComponent(cell);
setTimeout(() => {
setImmediate(() => {
done();
});
});

View File

@ -24,7 +24,7 @@ describe('Markdown component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});

View File

@ -25,7 +25,7 @@ describe('Output component', () => {
beforeEach(done => {
createComponent(json.cells[2].outputs[0]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -43,7 +43,7 @@ describe('Output component', () => {
beforeEach(done => {
createComponent(json.cells[3].outputs[0]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -73,7 +73,7 @@ describe('Output component', () => {
beforeEach(done => {
createComponent(json.cells[5].outputs[0]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -87,7 +87,7 @@ describe('Output component', () => {
beforeEach(done => {
createComponent(json.cells[6].outputs[0]);
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -104,7 +104,7 @@ describe('Output component', () => {
it("renders as plain text when doesn't recognise other types", done => {
createComponent(json.cells[7].outputs[0]);
setTimeout(() => {
setImmediate(() => {
expect(vm.$el.querySelector('pre')).not.toBeNull();
expect(vm.$el.textContent.trim()).toContain('testing');

View File

@ -16,7 +16,7 @@ describe('Prompt component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -40,7 +40,7 @@ describe('Prompt component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});

View File

@ -22,7 +22,7 @@ describe('Notebook component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -42,7 +42,7 @@ describe('Notebook component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
@ -74,7 +74,7 @@ describe('Notebook component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});

View File

@ -13,6 +13,8 @@ describe Projects::AlertManagementHelper do
let(:user_can_enable_alert_management) { true }
let(:setting_path) { edit_project_service_path(project, AlertsService) }
subject(:data) { helper.alert_management_data(current_user, project) }
before do
allow(helper)
.to receive(:can?)
@ -27,11 +29,33 @@ describe Projects::AlertManagementHelper do
'enable-alert-management-path' => setting_path,
'empty-alert-svg-path' => match_asset_path('/assets/illustrations/alert-management-empty-state.svg'),
'user-can-enable-alert-management' => 'true',
'alert-management-enabled' => 'true'
'alert-management-enabled' => 'false'
)
end
end
context 'with alerts service' do
let_it_be(:alerts_service) { create(:alerts_service, project: project) }
context 'when alerts service is active' do
it 'enables alert management' do
expect(data).to include(
'alert-management-enabled' => 'true'
)
end
end
context 'when alerts service is inactive' do
it 'disables alert management' do
alerts_service.update(active: false)
expect(data).to include(
'alert-management-enabled' => 'false'
)
end
end
end
context 'when user does not have requisite enablement permissions' do
let(:user_can_enable_alert_management) { false }

View File

@ -1,5 +0,0 @@
// 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';

View File

@ -1,5 +0,0 @@
// 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_discussions';

View File

@ -1,5 +0,0 @@
// 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';

View File

@ -1,5 +0,0 @@
// 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';

View File

@ -1,7 +0,0 @@
// 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
import getDiffWithCommit from '../../../frontend/diffs/mock_data/diff_with_commit';
export default getDiffWithCommit;

View File

@ -1,7 +0,0 @@
// 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
import diffsMockData from '../../../frontend/diffs/mock_data/merge_request_diffs';
export default diffsMockData;

View File

@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import initMRPage from '~/mr_notes/index';
import axios from '~/lib/utils/axios_utils';
import { userDataMock, notesDataMock, noteableDataMock } from '../../frontend/notes/mock_data';
import diffFileMockData from '../diffs/mock_data/diff_file';
import diffFileMockData from '../../frontend/diffs/mock_data/diff_file';
export default function initVueMRPage() {
const mrTestEl = document.createElement('div');

View File

@ -152,6 +152,148 @@ describe Projects::Prometheus::AlertPresenter do
end
end
end
context 'with embedded metrics' do
let(:starts_at) { '2018-03-12T09:06:00Z' }
shared_examples_for 'markdown with metrics embed' do
let(:expected_markdown) do
<<~MARKDOWN.chomp
#### Summary
**Start time:** #{presenter.starts_at}#{markdown_line_break}
**full_query:** `avg(metric) > 1.0`
[](#{url})
MARKDOWN
end
context 'without a starting time available' do
around do |example|
Timecop.freeze(starts_at) { example.run }
end
it { is_expected.to eq(expected_markdown) }
end
context 'with a starting time available' do
before do
payload['startsAt'] = starts_at
end
it { is_expected.to eq(expected_markdown) }
end
end
context 'for gitlab-managed prometheus alerts' do
let(:gitlab_alert) { create(:prometheus_alert, project: project) }
let(:metric_id) { gitlab_alert.prometheus_metric_id }
let(:env_id) { gitlab_alert.environment_id }
before do
payload['labels'] = { 'gitlab_alert_id' => metric_id }
end
let(:url) { "http://localhost/#{project.full_path}/prometheus/alerts/#{metric_id}/metrics_dashboard?end=2018-03-12T09%3A36%3A00Z&environment_id=#{env_id}&start=2018-03-12T08%3A36%3A00Z" }
it_behaves_like 'markdown with metrics embed'
end
context 'for alerts from a self-managed prometheus' do
let!(:environment) { create(:environment, project: project, name: 'production') }
let(:url) { "http://localhost/#{project.full_path}/-/environments/#{environment.id}/metrics_dashboard?embed_json=#{CGI.escape(embed_content.to_json)}&end=2018-03-12T09%3A36%3A00Z&start=2018-03-12T08%3A36%3A00Z" }
let(:title) { 'title' }
let(:y_label) { 'y_label' }
let(:query) { 'avg(metric) > 1.0' }
let(:embed_content) do
{
panel_groups: [{
panels: [{
type: 'line-graph',
title: title,
y_label: y_label,
metrics: [{ query_range: query }]
}]
}]
}
end
before do
# Setup embed time range
payload['startsAt'] = starts_at
# Setup query
payload['generatorURL'] = "http://host?g0.expr=#{CGI.escape(query)}"
# Setup environment
payload['labels'] ||= {}
payload['labels']['gitlab_environment_name'] = 'production'
# Setup chart title & axis labels
payload['annotations'] ||= {}
payload['annotations']['title'] = 'title'
payload['annotations']['gitlab_y_label'] = 'y_label'
end
it_behaves_like 'markdown with metrics embed'
context 'without y_label' do
let(:y_label) { title }
before do
payload['annotations'].delete('gitlab_y_label')
end
it_behaves_like 'markdown with metrics embed'
end
context 'when not enough information is present for an embed' do
let(:expected_markdown) do
<<~MARKDOWN.chomp
#### Summary
**Start time:** #{presenter.starts_at}#{markdown_line_break}
**full_query:** `avg(metric) > 1.0`
MARKDOWN
end
context 'without title' do
before do
payload['annotations'].delete('title')
end
it { is_expected.to eq(expected_markdown) }
end
context 'without environment' do
before do
payload['labels'].delete('gitlab_environment_name')
end
it { is_expected.to eq(expected_markdown) }
end
context 'without full_query' do
let(:expected_markdown) do
<<~MARKDOWN.chomp
#### Summary
**Start time:** #{presenter.starts_at}
MARKDOWN
end
before do
payload.delete('generatorURL')
end
it { is_expected.to eq(expected_markdown) }
end
end
end
end
end
describe '#show_performance_dashboard_link?' do