提交 5bdbc604 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 79ddf163
......@@ -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();
},
......
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]),
});
}
......@@ -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]
......
......@@ -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
......
......@@ -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
......
......@@ -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')
......@@ -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
......
---
title: Moves embedded metrics for Prometheus alerts to Core
merge_request: 31203
author:
type: changed
---
title: Adds URL parameter for confidential new issue creation
merge_request: 30250
author:
type: added
---
# 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\)\*\*)'
......@@ -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:
......
......@@ -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.
......
......@@ -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
......
......@@ -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
......
......@@ -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')
......
......@@ -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 ""
......
......@@ -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
......
......@@ -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)
......
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';
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);
function factory(routeArg) {
const localVue = createLocalVue();
localVue.use(VueRouter);
window.gon = { sprite_icons: '' };
window.gon = { sprite_icons: '' };
router = createRouter('/');
const router = createRouter('/');
if (routeArg !== undefined) {
router.push(routeArg);
}
vm = mount(App, {
localVue,
router,
mocks: {
$apollo: {
queries: {
designs: { loading: true },
design: { loading: true },
permissions: { loading: true },
},
return mount(App, {
localVue,
router,
mocks: {
$apollo: {
queries: {
designs: { loading: true },
design: { loading: true },
permissions: { loading: true },
},
},
});
}
beforeEach(() => {
factory();
},
});
}
afterEach(() => {
vm.destroy();
jest.mock('mousetrap', () => ({
bind: jest.fn(),
unbind: jest.fn(),
}));
router.app.$destroy();
describe('Design management router', () => {
afterEach(() => {
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');
});
......
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,
},
}),
);
});
});
});
......@@ -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();
......
......@@ -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();
});
});
......
......@@ -24,7 +24,7 @@ describe('Markdown component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
......
......@@ -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');
......
......@@ -16,7 +16,7 @@ describe('Prompt component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
......@@ -40,7 +40,7 @@ describe('Prompt component', () => {
});
vm.$mount();
setTimeout(() => {
setImmediate(() => {
done();
});
});
......
......@@ -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();
});
});
......
......@@ -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 }
......
// 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';
// 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';
// 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';
// 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';
// 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;
// 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;
......@@ -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');
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册