Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-03-05 09:07:09 +00:00
parent ddaba8dab1
commit 3c7a1efc2f
18 changed files with 199 additions and 256 deletions

View File

@ -491,7 +491,7 @@ lib/gitlab/checks/**
/doc/administration/git_protocol.md @msedlakjakubowski
/doc/administration/gitaly/ @eread
/doc/administration/housekeeping.md @eread
/doc/administration/inactive_project_deletion.md @eread
/doc/administration/inactive_project_deletion.md @lciutacu
/doc/administration/incoming_email.md @msedlakjakubowski
/doc/administration/index.md @axil
/doc/administration/instance_limits.md @axil
@ -670,7 +670,7 @@ lib/gitlab/checks/**
/doc/api/markdown.md @msedlakjakubowski
/doc/api/member_roles.md @jglassman1
/doc/api/members.md @jglassman1
/doc/api/merge_request_approvals.md @msedlakjakubowski
/doc/api/merge_request_approvals.md @aqualls
/doc/api/merge_request_context_commits.md @aqualls
/doc/api/merge_requests.md @aqualls
/doc/api/merge_trains.md @marcel.amirault @lyspin
@ -759,9 +759,12 @@ lib/gitlab/checks/**
/doc/ci/examples/deployment/ @phillipwells
/doc/ci/examples/semantic-release.md @phillipwells
/doc/ci/interactive_web_terminal/ @fneill
/doc/ci/jobs/ @marcel.amirault
/doc/ci/jobs/ci_job_token.md @marcel.amirault
/doc/ci/jobs/index.md @marcel.amirault @lyspin
/doc/ci/jobs/job_artifacts.md @marcel.amirault
/doc/ci/jobs/job_artifacts_troubleshooting.md @marcel.amirault
/doc/ci/jobs/job_control.md @marcel.amirault @lyspin
/doc/ci/jobs/job_troubleshooting.md @marcel.amirault @lyspin
/doc/ci/pipelines/pipeline_security.md @marcel.amirault
/doc/ci/resource_groups/ @phillipwells
/doc/ci/runners/ @fneill
@ -771,6 +774,7 @@ lib/gitlab/checks/**
/doc/ci/ssh_keys/ @marcel.amirault
/doc/ci/test_cases/ @msedlakjakubowski
/doc/ci/testing/code_quality.md @rdickenson
/doc/ci/testing/code_quality_troubleshooting.md @rdickenson
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/signing_examples.md @marcel.amirault
/doc/development/advanced_search.md @gitlab-org/search-team/migration-maintainers
@ -779,10 +783,10 @@ lib/gitlab/checks/**
/doc/development/avoiding_required_stops.md @gitlab-org/distribution
/doc/development/build_test_package.md @gitlab-org/distribution
/doc/development/cascading_settings.md @gitlab-org/govern/authentication/approvers
/doc/development/cells/ @gitlab-org/tenant-scale-group/backend-engineers
/doc/development/cells/ @abdwdd @alexpooley @manojmj
/doc/development/cicd/ @gitlab-org/maintainers/cicd-verify
/doc/development/contributing/verify/ @gitlab-org/maintainers/cicd-verify
/doc/development/database/ @gitlab-org/maintainers/database
/doc/development/database/ @abdwdd @alexpooley @manojmj
/doc/development/distribution/ @gitlab-org/distribution
/doc/development/documentation/ @sselhorn
/doc/development/fe_guide/customizable_dashboards.md @gitlab-org/analytics-section/product-analytics/engineers/frontend
@ -792,11 +796,11 @@ lib/gitlab/checks/**
/doc/development/gitaly.md @proglottis @toon
/doc/development/gitlab_flavored_markdown/ @gitlab-org/maintainers/remote-development/backend @gitlab-org/maintainers/remote-development/frontend
/doc/development/gitpod_internals.md @gl-quality/eng-prod
/doc/development/image_scaling.md @gitlab-org/tenant-scale-group/backend-engineers
/doc/development/image_scaling.md @abdwdd @alexpooley @manojmj
/doc/development/internal_analytics/ @gitlab-org/analytics-section/product-analytics/engineers/frontend @gitlab-org/analytics-section/analytics-instrumentation/engineers
/doc/development/navigation_sidebar.md @gitlab-org/manage/foundations/engineering
/doc/development/omnibus.md @gitlab-org/distribution
/doc/development/organization/ @gitlab-org/tenant-scale-group/backend-engineers
/doc/development/organization/ @abdwdd @alexpooley @manojmj
/doc/development/permissions.md @gitlab-org/govern/authentication/approvers
/doc/development/permissions/ @gitlab-org/govern/authentication/approvers
/doc/development/permissions/custom_roles.md @gitlab-org/govern/authorization/approvers
@ -855,6 +859,7 @@ lib/gitlab/checks/**
/doc/tutorials/dependency_scanning.md @rdickenson
/doc/tutorials/export_sbom.md @rdickenson
/doc/tutorials/fuzz_testing/ @rdickenson
/doc/tutorials/idea_management/ @msedlakjakubowski
/doc/tutorials/install_gitlab_single_node/ @axil
/doc/tutorials/issue_triage/ @msedlakjakubowski
/doc/tutorials/move_personal_project_to_group/ @lciutacu
@ -862,6 +867,7 @@ lib/gitlab/checks/**
/doc/tutorials/protected_workflow/ @aqualls
/doc/tutorials/scan_execution_policy/ @rdickenson
/doc/tutorials/scan_result_policy/ @rdickenson
/doc/tutorials/scrum_events/ @msedlakjakubowski
/doc/tutorials/update_commit_messages/ @msedlakjakubowski
/doc/tutorials/website_project_with_analytics/ @lciutacu
/doc/update/ @axil
@ -880,9 +886,11 @@ lib/gitlab/checks/**
/doc/user/emoji_reactions.md @msedlakjakubowski
/doc/user/enterprise_user/ @jglassman1
/doc/user/gitlab_duo_chat.md @sselhorn
/doc/user/gitlab_duo_examples.md @sselhorn
/doc/user/group/access_and_permissions.md @lciutacu
/doc/user/group/clusters/ @phillipwells
/doc/user/group/compliance_frameworks.md @eread
/doc/user/group/compliance_pipelines.md @eread
/doc/user/group/contribution_analytics/ @lciutacu
/doc/user/group/custom_project_templates.md @msedlakjakubowski
/doc/user/group/devops_adoption/ @lciutacu

View File

@ -1 +1 @@
f508b901207c1e6d2d5bcb8da631b5ad815aa483
f68eed8a9d41bfe20840e87c8a9f6cd58db30de3

View File

@ -129,12 +129,16 @@ export default {
<div
class="filter-dropdown-container gl-md-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-align-items-flex-start"
>
<toggle-labels />
<toggle-epics-swimlanes
v-if="swimlanesFeatureAvailable && isSignedIn"
:is-swimlanes-on="isSwimlanesOn"
@toggleSwimlanes="$emit('toggleSwimlanes', $event)"
/>
<div
class="gl-display-flex gl-flex-direction-row gl-sm-align-items-flex-start gl-xs-justify-content-end gl-flex-wrap gl-md-flex-nowrap"
>
<toggle-labels />
<toggle-epics-swimlanes
v-if="swimlanesFeatureAvailable && isSignedIn"
:is-swimlanes-on="isSwimlanesOn"
@toggleSwimlanes="$emit('toggleSwimlanes', $event)"
/>
</div>
<config-toggle @showBoardModal="setCurrentForm" />
<board-add-new-column-trigger
v-if="canAdminList"

View File

@ -88,7 +88,7 @@ export default {
message: __('Failed to load error details from Sentry.'),
}),
result(res) {
if (res.data.project?.sentryErrors?.detailedError) {
if (res.data?.project?.sentryErrors?.detailedError) {
this.$apollo.queries.error.stopPolling();
this.setStatus(this.error.status);
} else {

View File

@ -39,7 +39,7 @@ export default {
</script>
<template>
<div class="board-labels-toggle-wrapper gl-display-flex gl-align-items-center gl-ml-3 gl-h-7">
<div class="board-labels-toggle-wrapper gl-display-flex gl-align-items-center gl-md-ml-3 gl-h-7">
<local-storage-sync
:value="isShowingLabels"
storage-key="gl-show-board-labels"

View File

@ -1,8 +0,0 @@
---
name: sidekiq_job_completion_metric_initialize
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64637
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1133
milestone: '14.1'
type: development
group: group::scalability
default_enabled: true

View File

@ -1,7 +1,7 @@
---
name: two_factor_for_cli
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39703
rollout_issue_url:
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/443750
milestone: '13.5'
type: development
group: group::authentication

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddIndexEnvironmentsNameWithoutType < Gitlab::Database::Migration[2.2]
milestone '16.10'
disable_ddl_transaction!
INDEX_NAME = 'index_environments_name_without_type'
def up
add_concurrent_index :environments,
"project_id, lower(ltrim(ltrim(name, environment_type), '/')) varchar_pattern_ops, state", name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :environments, INDEX_NAME
end
end

View File

@ -0,0 +1 @@
f7bdc5bb248a273714916d5426177de7f583e55b17188c858caae37e8e3d617c

View File

@ -24914,6 +24914,8 @@ CREATE INDEX index_environments_cluster_agent_id ON environments USING btree (cl
CREATE INDEX index_environments_for_name_search_within_folder ON environments USING btree (project_id, lower(ltrim((name)::text, ((environment_type)::text || '/'::text))) varchar_pattern_ops, state);
CREATE INDEX index_environments_name_without_type ON environments USING btree (project_id, lower(ltrim(ltrim((name)::text, (environment_type)::text), '/'::text)) varchar_pattern_ops, state);
CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id);
CREATE INDEX index_environments_on_name_varchar_pattern_ops ON environments USING btree (name varchar_pattern_ops);

View File

@ -1,6 +1,6 @@
---
stage: none
group: unassigned
stage: AI-powered
group: AI Framework
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -1,6 +1,6 @@
---
stage: Data Science
group: ModelOps
stage: ModelOps
group: MLOps
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -1,6 +1,6 @@
---
stage: Data Science
group: ModelOps
stage: ModelOps
group: MLOps
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -15,6 +15,12 @@ GitLab can integrate with [Kerberos](https://web.mit.edu/kerberos/) as an authen
- You can configure GitLab so your users can sign in with their Kerberos credentials.
- You can use Kerberos to [prevent](https://web.mit.edu/sipb/doc/working/guide/guide/node20.html) anyone from intercepting or eavesdropping on the transmitted password.
Kerberos is only available on instances that use GitLab Enterprise Edition (EE). To use Kerberos, you can do one of the following:
- [Activate GitLab EE](../administration/license.md#activate-gitlab-ee) for your instance.
- If you have set up a GitLab Community Edition (CE) instance using the Linux
package, [convert from GitLab CE to GitLab EE](../update/package/convert_to_ee.md).
WARNING:
GitLab CI/CD doesn't work with a Kerberos-enabled GitLab instance unless the integration is
[set to use a dedicated port](#http-git-access-with-kerberos-token-passwordless-authentication).

View File

@ -66,8 +66,6 @@ module Gitlab
metrics[:sidekiq_concurrency].set({}, Sidekiq.default_configuration[:concurrency].to_i)
return unless ::Feature.enabled?(:sidekiq_job_completion_metric_initialize)
possible_sli_labels = []
::Gitlab::SidekiqConfig.current_worker_queue_mappings.each do |worker, queue|
worker_class = worker.safe_constantize

View File

@ -25,16 +25,17 @@ namespace :tw do
CodeOwnerRule.new('AI Model Validation', '@sselhorn'),
# CodeOwnerRule.new('Analytics Instrumentation', ''),
CodeOwnerRule.new('Anti-Abuse', '@phillipwells'),
CodeOwnerRule.new('Cloud Connector', '@jglassman1'),
CodeOwnerRule.new('Authentication', '@jglassman1'),
CodeOwnerRule.new('Authorization', '@jglassman1'),
# CodeOwnerRule.new('Billing and Subscription Management', ''),
CodeOwnerRule.new('Cloud Connector', '@jglassman1'),
CodeOwnerRule.new('Code Creation', '@jglassman1'),
CodeOwnerRule.new('Code Review', '@aqualls'),
CodeOwnerRule.new('Compliance', '@eread'),
CodeOwnerRule.new('Composition Analysis', '@rdickenson'),
CodeOwnerRule.new('Container Registry', '@marcel.amirault'),
CodeOwnerRule.new('Contributor Experience', '@eread'),
CodeOwnerRule.new('Custom Models', '@sselhorn'),
# CodeOwnerRule.new('Database', ''),
CodeOwnerRule.new('DataOps', '@sselhorn'),
# CodeOwnerRule.new('Delivery', ''),

View File

@ -12,8 +12,12 @@ import {
} from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getErrorDetailsQuery from '~/error_tracking/queries/details.query.graphql';
import { severityLevel, severityLevelVariant, errorStatus } from '~/error_tracking/constants';
import ErrorDetails from '~/error_tracking/components/error_details.vue';
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
@ -27,13 +31,61 @@ jest.mock('~/alert');
jest.mock('~/tracking');
Vue.use(Vuex);
Vue.use(VueApollo);
const defaultError = {
id: 'gid://gitlab/Gitlab::ErrorTracking::DetailedError/129381',
sentryId: 129381,
title: 'Issue title',
userCount: 2,
count: 12,
status: 'open',
firstSeen: '2017-05-26T13:32:48Z',
lastSeen: '2018-05-26T13:32:48Z',
message: 'Error',
culprit: 'Error',
tags: {
level: 'high',
logger: 'ruby',
},
externalUrl: 'http://sentry.gitlab.net/gitlab',
externalBaseUrl: 'https://gitlab.com',
firstReleaseVersion: 1,
frequency: null,
lastReleaseVersion: 2,
gitlabCommit: '12345678',
gitlabCommitPath: '/commit/12345678',
gitlabIssuePath: '/issues/1',
integrated: true,
};
describe('ErrorDetails', () => {
let store;
let wrapper;
let actions;
let getters;
let mocks;
let requestHandlers;
const mockApolloHandlers = ({ detailedError, getErrorDetailsQueryHandler }) => {
return {
getErrorDetailsQuery:
getErrorDetailsQueryHandler ||
jest.fn().mockResolvedValue({
data: {
id: 1,
project: {
id: 2,
sentryErrors: { id: 3, detailedError: { ...defaultError, ...detailedError } },
},
},
}),
};
};
const createMockApolloProvider = (handlers) => {
requestHandlers = handlers;
return createMockApollo([[getErrorDetailsQuery, requestHandlers.getErrorDetailsQuery]]);
};
const findInput = (name) => {
const inputs = wrapper
@ -48,8 +100,15 @@ describe('ErrorDetails', () => {
wrapper.find('[data-testid="update-resolve-status-btn"]');
const findAlert = () => wrapper.findComponent(GlAlert);
function mountComponent({ integratedErrorTrackingEnabled = false } = {}) {
const createComponent = async ({
integratedErrorTrackingEnabled = false,
getErrorDetailsQueryHandler = null,
detailedError = {},
} = {}) => {
wrapper = shallowMount(ErrorDetails, {
apolloProvider: createMockApolloProvider(
mockApolloHandlers({ detailedError, getErrorDetailsQueryHandler }),
),
stubs: {
GlButton,
GlSprintf,
@ -58,7 +117,6 @@ describe('ErrorDetails', () => {
GlDisclosureDropdownGroup,
},
store,
mocks,
propsData: {
issueId: '123',
projectPath: '/root/gitlab-test',
@ -70,11 +128,14 @@ describe('ErrorDetails', () => {
integratedErrorTrackingEnabled,
},
});
}
await waitForPromises();
};
beforeEach(() => {
actions = {
startPollingStacktrace: () => {},
setStatus: jest.fn().mockImplementation(() => {}),
startPollingStacktrace: jest.fn().mockImplementation(() => {}),
updateIgnoreStatus: jest.fn().mockResolvedValue({}),
updateResolveStatus: jest.fn().mockResolvedValue({ closed_issue_iid: 1 }),
};
@ -85,7 +146,7 @@ describe('ErrorDetails', () => {
};
const state = {
stacktraceData: {},
stacktraceData: { date_received: '2020-01-01' },
loadingStacktrace: true,
errorStatus: '',
};
@ -100,28 +161,11 @@ describe('ErrorDetails', () => {
},
},
});
const query = jest.fn();
mocks = {
$apollo: {
query,
queries: {
error: {
loading: true,
stopPolling: jest.fn(),
setOptions: jest.fn(),
},
},
},
};
});
describe('loading', () => {
beforeEach(() => {
mountComponent();
});
it('should show spinner while loading', () => {
createComponent();
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.findComponent(GlLink).exists()).toBe(false);
expect(wrapper.findComponent(Stacktrace).exists()).toBe(false);
@ -132,76 +176,42 @@ describe('ErrorDetails', () => {
const initTime = 300000;
const endTime = initTime + 10000;
beforeEach(() => {
mocks.$apollo.queries.error.loading = false;
jest.spyOn(Date, 'now').mockReturnValue(initTime);
mountComponent();
});
it('when before timeout, still shows loading', async () => {
Date.now.mockReturnValue(endTime - 1);
jest
.spyOn(Date, 'now')
.mockReturnValueOnce(initTime)
.mockReturnValueOnce(endTime - 1);
wrapper.vm.onNoApolloResult();
await nextTick();
await createComponent({ getErrorDetailsQueryHandler: jest.fn().mockRejectedValue({}) });
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
expect(createAlert).not.toHaveBeenCalled();
expect(mocks.$apollo.queries.error.stopPolling).not.toHaveBeenCalled();
expect(createAlert).toHaveBeenCalledTimes(1);
});
it('when timeout is hit and no apollo result, stops loading and shows alert', async () => {
Date.now.mockReturnValue(endTime + 1);
wrapper.vm.onNoApolloResult();
await nextTick();
jest
.spyOn(Date, 'now')
.mockReturnValueOnce(initTime)
.mockReturnValueOnce(endTime + 1);
await createComponent({ getErrorDetailsQueryHandler: jest.fn().mockRejectedValue({}) });
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.findComponent(GlLink).exists()).toBe(false);
expect(createAlert).toHaveBeenCalledWith({
expect(createAlert).toHaveBeenCalledTimes(2);
expect(createAlert).toHaveBeenLastCalledWith({
message: 'Could not connect to Sentry. Refresh the page to try again.',
variant: VARIANT_WARNING,
});
expect(mocks.$apollo.queries.error.stopPolling).toHaveBeenCalled();
});
});
describe('Error details', () => {
beforeEach(() => {
mocks.$apollo.queries.error.loading = false;
mountComponent();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
id: 'gid://gitlab/Gitlab::ErrorTracking::DetailedError/129381',
sentryId: 129381,
title: 'Issue title',
externalUrl: 'http://sentry.gitlab.net/gitlab',
firstSeen: '2017-05-26T13:32:48Z',
lastSeen: '2018-05-26T13:32:48Z',
count: 12,
userCount: 2,
},
stacktraceData: {
date_received: '2020-05-20',
},
});
});
describe('unsafe chars for culprit field', () => {
const findReportedText = () => wrapper.find('[data-testid="reported-text"]');
const culprit = '<script>console.log("surprise!")</script>';
beforeEach(() => {
beforeEach(async () => {
store.state.details.loadingStacktrace = false;
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
culprit,
},
});
const detailedError = { culprit };
await createComponent({ detailedError });
});
it('should not convert interpolated text to html entities', () => {
@ -216,43 +226,22 @@ describe('ErrorDetails', () => {
describe('Badges', () => {
it('should show language and error level badges', async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
tags: { level: 'error', logger: 'ruby' },
},
});
await nextTick();
const detailedError = { tags: { level: 'error', logger: 'ruby' } };
await createComponent({ detailedError });
expect(wrapper.findAllComponents(GlBadge).length).toBe(2);
});
it('should NOT show the badge if the tag is not present', async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
tags: { level: 'error' },
},
});
await nextTick();
const detailedError = { tags: { level: 'error', logger: null } };
await createComponent({ detailedError });
expect(wrapper.findAllComponents(GlBadge).length).toBe(1);
});
it.each(Object.keys(severityLevel))(
'should set correct severity level variant for %s badge',
async (level) => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
tags: { level: severityLevel[level] },
},
});
await nextTick();
const detailedError = { tags: { level: severityLevel[level], logger: null } };
await createComponent({ detailedError });
expect(wrapper.findComponent(GlBadge).props('variant')).toEqual(
severityLevelVariant[severityLevel[level]],
);
@ -260,15 +249,8 @@ describe('ErrorDetails', () => {
);
it('should fallback for ERROR severityLevelVariant when severityLevel is unknown', async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
tags: { level: 'someNewErrorLevel' },
},
});
await nextTick();
const detailedError = { tags: { level: 'someNewErrorLevel', logger: null } };
await createComponent({ detailedError });
expect(wrapper.findComponent(GlBadge).props('variant')).toEqual(
severityLevelVariant[severityLevel.ERROR],
);
@ -277,6 +259,7 @@ describe('ErrorDetails', () => {
describe('ErrorDetailsInfo', () => {
it('should show ErrorDetailsInfo', async () => {
await createComponent();
store.state.details.loadingStacktrace = false;
await nextTick();
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
@ -286,25 +269,19 @@ describe('ErrorDetails', () => {
});
describe('timeline chart', () => {
it('should not show timeline chart if frequency data does not exist', () => {
it('should not show timeline chart if frequency data does not exist', async () => {
await createComponent();
expect(wrapper.findComponent(TimelineChart).exists()).toBe(false);
expect(wrapper.text()).not.toContain('Last 24 hours');
});
it('should show timeline chart', async () => {
const mockFrequency = [
[0, 1],
[2, 3],
{ count: 0, time: 1 },
{ count: 2, time: 3 },
];
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
frequency: mockFrequency,
},
});
await nextTick();
const detailedError = { frequency: mockFrequency };
await createComponent({ detailedError });
expect(wrapper.findComponent(TimelineChart).exists()).toBe(true);
expect(wrapper.findComponent(TimelineChart).props('timelineData')).toEqual(mockFrequency);
expect(wrapper.text()).toContain('Last 24 hours');
@ -312,6 +289,10 @@ describe('ErrorDetails', () => {
});
describe('Stacktrace', () => {
beforeEach(async () => {
await createComponent();
});
it('should show stacktrace', async () => {
store.state.details.loadingStacktrace = false;
await nextTick();
@ -331,6 +312,10 @@ describe('ErrorDetails', () => {
});
describe('When a user clicks the create issue button', () => {
beforeEach(async () => {
await createComponent({ detailedError: { gitlabIssuePath: '' } });
});
it('should send sentry_issue_identifier', () => {
const sentryErrorIdInput = findInput(
'issue[sentry_issue_attributes][sentry_issue_identifier]',
@ -364,8 +349,8 @@ describe('ErrorDetails', () => {
describe('when error is unresolved', () => {
beforeEach(async () => {
await createComponent();
store.state.details.errorStatus = errorStatus.UNRESOLVED;
await nextTick();
});
@ -391,8 +376,8 @@ describe('ErrorDetails', () => {
describe('when error is ignored', () => {
beforeEach(async () => {
await createComponent();
store.state.details.errorStatus = errorStatus.IGNORED;
await nextTick();
});
@ -418,8 +403,8 @@ describe('ErrorDetails', () => {
describe('when error is resolved', () => {
beforeEach(async () => {
await createComponent();
store.state.details.errorStatus = errorStatus.RESOLVED;
await nextTick();
});
@ -443,18 +428,12 @@ describe('ErrorDetails', () => {
});
it('should show alert with closed issueId', async () => {
const closedIssueId = 123;
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
isAlertVisible: true,
closedIssueId,
});
await nextTick();
await findUpdateResolveStatusButton().vm.$emit('click');
await waitForPromises();
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toContain(`#${closedIssueId}`);
expect(findAlert().text()).toBe(
'The associated issue #1 has been closed as the error is now resolved.',
);
});
});
});
@ -466,15 +445,9 @@ describe('ErrorDetails', () => {
const findViewIssueButton = () => wrapper.find('[data-testid="view-issue-button"]');
describe('is present', () => {
beforeEach(() => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
gitlabIssuePath,
},
});
beforeEach(async () => {
const detailedError = { gitlabIssuePath };
await createComponent({ detailedError });
});
it('should display the View issue button', () => {
@ -491,15 +464,9 @@ describe('ErrorDetails', () => {
});
describe('is not present', () => {
beforeEach(() => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {
gitlabIssuePath: null,
},
});
beforeEach(async () => {
const detailedError = { gitlabIssuePath: null };
await createComponent({ detailedError });
});
it('should not display the View issue button', () => {
@ -518,55 +485,44 @@ describe('ErrorDetails', () => {
});
describe('Snowplow tracking', () => {
beforeEach(() => {
mocks.$apollo.queries.error.loading = false;
});
describe.each([true, false])(`when integratedErrorTracking is %s`, (integrated) => {
describe.each`
integrated | variant
${true} | ${'integrated'}
${false} | ${'external'}
`(`when integratedErrorTracking is $integrated`, ({ integrated, variant }) => {
const category = 'Error Tracking';
beforeEach(() => {
mountComponent({ integratedErrorTrackingEnabled: integrated });
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// TODO remove setData usage https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2216
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
error: {},
beforeEach(async () => {
await createComponent({
integratedErrorTrackingEnabled: integrated,
detailedError: { gitlabIssuePath: '' },
});
});
it('should track detail page views', () => {
expect(Tracking.event).toHaveBeenCalledWith(category, 'view_error_details', {
extra: {
variant: integrated ? 'integrated' : 'external',
},
extra: { variant },
});
});
it('should track IGNORE status update', async () => {
await findUpdateIgnoreStatusButton().trigger('click');
expect(Tracking.event).toHaveBeenCalledWith(category, 'update_ignored_status', {
extra: {
variant: integrated ? 'integrated' : 'external',
},
extra: { variant },
});
});
it('should track RESOLVE status update', async () => {
await findUpdateResolveStatusButton().trigger('click');
expect(Tracking.event).toHaveBeenCalledWith(category, 'update_resolved_status', {
extra: {
variant: integrated ? 'integrated' : 'external',
},
extra: { variant },
});
});
it('should track create issue button click', async () => {
await wrapper.find('[data-testid="create-issue-button"]').vm.$emit('click');
expect(Tracking.event).toHaveBeenCalledWith(category, 'click_create_issue_from_error', {
extra: {
variant: integrated ? 'integrated' : 'external',
},
extra: { variant },
});
});
});

View File

@ -72,15 +72,6 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics, feature_category: :shar
end
end
shared_examples "not initializing sidekiq SLIs" do
it 'does not initialize sidekiq SLIs' do
expect(Gitlab::Metrics::SidekiqSlis)
.not_to receive(initialize_sli_method)
described_class.initialize_process_metrics
end
end
context 'initializing execution and queueing SLIs' do
before do
allow(Gitlab::SidekiqConfig)
@ -115,40 +106,6 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics, feature_category: :shar
described_class.initialize_process_metrics
end
end
context 'when the sidekiq_job_completion_metric_initialize feature flag is disabled' do
before do
stub_feature_flags(sidekiq_job_completion_metric_initialize: false)
end
it 'sets the concurrency metric' do
expect(concurrency_metric).to receive(:set).with({}, Sidekiq.default_configuration[:concurrency].to_i)
described_class.initialize_process_metrics
end
it 'does not initialize sidekiq_jobs_completion_seconds' do
allow(Gitlab::SidekiqConfig)
.to receive(:current_worker_queue_mappings)
.and_return('MergeWorker' => 'merge', 'Ci::BuildFinishedWorker' => 'default')
expect(completion_seconds_metric).not_to receive(:get)
described_class.initialize_process_metrics
end
context 'sidekiq execution SLIs' do
let(:initialize_sli_method) { :initialize_execution_slis! }
it_behaves_like 'not initializing sidekiq SLIs'
end
context 'sidekiq queueing SLIs' do
let(:initialize_sli_method) { :initialize_queueing_slis! }
it_behaves_like 'not initializing sidekiq SLIs'
end
end
end
describe '#call' do