Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5b5ff31460
commit
3f8e9ba69e
|
|
@ -31,11 +31,6 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
showDetailsLink: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -56,7 +51,7 @@ export default {
|
|||
},
|
||||
|
||||
showDetails() {
|
||||
return this.showDetailsLink && Boolean(this.detailsPathWithId) && this.hasFailures;
|
||||
return Boolean(this.detailsPathWithId) && this.hasFailures;
|
||||
},
|
||||
|
||||
detailsPathWithId() {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import { createAlert } from '~/alert';
|
|||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import { s__, __, n__, sprintf } from '~/locale';
|
||||
import { HTTP_STATUS_TOO_MANY_REQUESTS } from '~/lib/utils/http_status';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
|
||||
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||
import { getGroupPathAvailability } from '~/rest_api';
|
||||
|
|
@ -69,7 +68,6 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
sourceUrl: {
|
||||
type: String,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import {
|
||||
GlButton,
|
||||
GlEmptyState,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
|
|
@ -22,7 +21,6 @@ import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
|
|||
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
import { isImporting } from '../utils';
|
||||
import { DEFAULT_ERROR } from '../utils/error_messages';
|
||||
|
|
@ -43,7 +41,6 @@ const tableCell = (config) => ({
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlEmptyState,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
|
|
@ -59,8 +56,6 @@ export default {
|
|||
GlTooltip,
|
||||
},
|
||||
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
|
||||
inject: ['realtimeChangesPath'],
|
||||
|
||||
data() {
|
||||
|
|
@ -107,10 +102,6 @@ export default {
|
|||
.map((item) => item.bulk_import_id);
|
||||
},
|
||||
|
||||
showDetailsLink() {
|
||||
return this.glFeatures.bulkImportDetailsPage;
|
||||
},
|
||||
|
||||
paginationConfigCopy() {
|
||||
return { ...this.paginationConfig };
|
||||
},
|
||||
|
|
@ -265,7 +256,7 @@ export default {
|
|||
<template #cell(created_at)="{ value }">
|
||||
<time-ago :time="value" />
|
||||
</template>
|
||||
<template #cell(status)="{ value, item, toggleDetails, detailsShowing }">
|
||||
<template #cell(status)="{ value, item }">
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-align-items-flex-start gl-justify-content-space-between gl-gap-3"
|
||||
>
|
||||
|
|
@ -273,20 +264,10 @@ export default {
|
|||
:id="item.bulk_import_id"
|
||||
:entity-id="item.id"
|
||||
:has-failures="item.has_failures"
|
||||
:show-details-link="showDetailsLink"
|
||||
:status="value"
|
||||
/>
|
||||
<gl-button
|
||||
v-if="!showDetailsLink && item.failures.length"
|
||||
:selected="detailsShowing"
|
||||
@click="toggleDetails"
|
||||
>{{ __('Details') }}</gl-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template #row-details="{ item }">
|
||||
<pre><code>{{ item.failures }}</code></pre>
|
||||
</template>
|
||||
</gl-table-lite>
|
||||
<pagination-bar
|
||||
:page-info="pageInfo"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export default {
|
|||
:id="htmlId"
|
||||
v-gl-tooltip.viewport="warningMessage"
|
||||
data-name="warning"
|
||||
data-testid="warning"
|
||||
class="gl-ml-2"
|
||||
/>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ class Import::BulkImportsController < ApplicationController
|
|||
before_action :ensure_bulk_import_enabled
|
||||
before_action :verify_blocked_uri, only: :status
|
||||
|
||||
before_action only: [:history] do
|
||||
push_frontend_feature_flag(:bulk_import_details_page)
|
||||
end
|
||||
|
||||
feature_category :importers
|
||||
urgency :low
|
||||
|
||||
|
|
@ -53,9 +49,7 @@ class Import::BulkImportsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def details
|
||||
render_404 unless Feature.enabled?(:bulk_import_details_page)
|
||||
end
|
||||
def details; end
|
||||
|
||||
def create
|
||||
return render json: { success: false }, status: :too_many_requests if throttled_request?
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: bulk_import_details_page
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135004
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429109
|
||||
milestone: '16.6'
|
||||
name: agent_registry
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140859
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437540
|
||||
milestone: '16.8'
|
||||
type: development
|
||||
group: group::import and integrate
|
||||
default_enabled: true
|
||||
group: "group::mlops"
|
||||
default_enabled: false
|
||||
|
|
@ -23994,7 +23994,7 @@ Returns [`PipelineSecurityReportFinding`](#pipelinesecurityreportfinding).
|
|||
|
||||
##### `Pipeline.securityReportFindings`
|
||||
|
||||
Vulnerability findings reported on the pipeline.
|
||||
Vulnerability findings reported on the pipeline. By default all the states except dismissed are included in the response.
|
||||
|
||||
Returns [`PipelineSecurityReportFindingConnection`](#pipelinesecurityreportfindingconnection).
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: Observability
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Metrics **(ULTIMATE SAAS EXPERIMENT)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124966) in GitLab 16.7 [with a flag](../administration/feature_flags.md) named `observability_metrics`. Disabled by default. This feature is an [Experiment](../policy/experiment-beta-support.md#experiment).
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `observability_metrics`.
|
||||
On GitLab.com, this feature is not available.
|
||||
The feature is not ready for production use.
|
||||
|
||||
Metrics provide insight about the operational health of monitored systems.
|
||||
Use metrics to learn more about your systems and applications in a given time range.
|
||||
|
||||
Metrics are structured as time series data, and are:
|
||||
|
||||
- Indexed by timestamp
|
||||
- Continuously expanding as additional data is gathered
|
||||
- Usually aggregated, downsampled, and queried by range
|
||||
- Have write-intensive requirements
|
||||
|
||||
## Configure metrics
|
||||
|
||||
Configure metrics to enable them for a project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
You must have at least the Maintainer role for the project.
|
||||
|
||||
1. Create an access token and enable metrics:
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > Access Tokens**.
|
||||
1. Create an access token with the following scopes: `read_api`, `read_observability`, `write_observability`. Be sure to save the access token value for later.
|
||||
1. Select **Monitor > Tracing**, and then select **Enable**.
|
||||
1. To configure your application to send GitLab metrics, set the following environment variables:
|
||||
|
||||
```shell
|
||||
OTEL_EXPORTER = "otlphttp"
|
||||
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "https://observe.gitlab.com/v3/<namespace-id>/<gitlab-project-id>/ingest/metrics"
|
||||
OTEL_EXPORTER_OTLP_METRICS_HEADERS = "PRIVATE-TOKEN=<gitlab-access-token>"
|
||||
```
|
||||
|
||||
Use the following values:
|
||||
|
||||
- `namespace-id` - The top-level group ID that contains the project
|
||||
- `gitlab-project-id` - The project ID
|
||||
- `gitlab-access-token` - The access token you created
|
||||
|
||||
Metrics are configured for your project.
|
||||
When you run your application, the OpenTelemetry exporter sends metrics to GitLab.
|
||||
|
||||
## View metrics
|
||||
|
||||
You can view the metrics for a given project:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Monitor > Metrics**.
|
||||
|
||||
A list of metrics is displayed.
|
||||
Select a metric to view its details.
|
||||
|
||||

|
||||
|
||||
### Metric details
|
||||
|
||||
Metrics are displayed as either a sum, a gauge, or a histogram.
|
||||
The metric details page displays a chart depending on the type of metric.
|
||||
|
||||
On the metric details page, you can also view a metric for a specific time range.
|
||||
|
||||

|
||||
|
|
@ -121,7 +121,7 @@ This rule enforces the defined actions based on security scan findings.
|
|||
| `scanners` | `array` of `string` | true | `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing` | The security scanners for this rule to consider. `sast` includes results from both SAST and SAST IaC scanners. |
|
||||
| `vulnerabilities_allowed` | `integer` | true | Greater than or equal to zero | Number of vulnerabilities allowed before this rule is considered. |
|
||||
| `severity_levels` | `array` of `string` | true | `info`, `unknown`, `low`, `medium`, `high`, `critical` | The severity levels for this rule to consider. |
|
||||
| `vulnerability_states` | `array` of `string` | true | `newly_detected`, `detected`, `confirmed`, `resolved`, `dismissed`, `new_needs_triage`, `new_dismissed` | All vulnerabilities fall into two categories:<br><br>**Newly Detected Vulnerabilities** - the `newly_detected` policy option covers vulnerabilities identified in the merge request branch itself but that do not currently exist on the default branch. This policy option requires a pipeline to complete before the rule is evaluated so that it knows whether vulnerabilities are newly detected or not. Merge requests are blocked until the pipeline and necessary security scans are complete. The `newly_detected` option considers both of the following statuses:<br><br> • Detected<br> • Dismissed<br><br> The `new_needs_triage` option considers the status<br><br> • Detected<br><br> The `new_dismissed` option considers the status<br><br> • Dismissed<br><br>**Pre-Existing Vulnerabilities** - these policy options are evaluated immediately and do not require a pipeline complete as they consider only vulnerabilities previously detected in the default branch.<br><br> • `Detected` - the policy looks for vulnerabilities in the detected state.<br> • `Confirmed` - the policy looks for vulnerabilities in the confirmed state.<br> • `Dismissed` - the policy looks for vulnerabilities in the dismissed state.<br> • `Resolved` - the policy looks for vulnerabilities in the resolved state. |
|
||||
| `vulnerability_states` | `array` of `string` | true | `[]` or `newly_detected`, `detected`, `confirmed`, `resolved`, `dismissed`, `new_needs_triage`, `new_dismissed` | All vulnerabilities fall into two categories:<br><br>**Newly Detected Vulnerabilities** - the `newly_detected` policy option covers vulnerabilities identified in the merge request branch itself but that do not currently exist on the default branch. This policy option requires a pipeline to complete before the rule is evaluated so that it knows whether vulnerabilities are newly detected or not. Merge requests are blocked until the pipeline and necessary security scans are complete. The `newly_detected` option considers both of the following statuses:<br><br> • Detected<br> • Dismissed<br><br> The `new_needs_triage` option considers the status<br><br> • Detected<br><br> The `new_dismissed` option considers the status<br><br> • Dismissed<br><br>**Pre-Existing Vulnerabilities** - these policy options are evaluated immediately and do not require a pipeline complete as they consider only vulnerabilities previously detected in the default branch.<br><br> • `Detected` - the policy looks for vulnerabilities in the detected state.<br> • `Confirmed` - the policy looks for vulnerabilities in the confirmed state.<br> • `Dismissed` - the policy looks for vulnerabilities in the dismissed state.<br> • `Resolved` - the policy looks for vulnerabilities in the resolved state. <br><br>An empty array, `[]`, covers the same statuses as `newly_detected`. It is equivalent to specifying `['new_needs_triage', 'new_dismissed']`. |
|
||||
| `vulnerability_attributes` | `object` | false | `{false_positive: boolean, fix_available: boolean}` | All vulnerability findings are considered by default. But filters can be applied for attributes to consider only vulnerability findings: <br><br> • With a fix available (`fix_available: true`)<br><br> • With no fix available (`fix_available: false`)<br> • That are false positive (`false_positive: true`)<br> • That are not false positive (`false_positive: false`)<br> • Or a combination of both. For example (`fix_available: true, false_positive: false`) |
|
||||
| `vulnerability_age` | `object` | false | N/A | Filter pre-existing vulnerability findings by age. A vulnerability's age is calculated as the time since it was detected in the project. The criteria are `operator`, `value`, and `interval`.<br>- The `operator` criterion specifies if the age comparison used is older than (`greater_than`) or younger than (`less_than`).<br>- The `value` criterion specifies the numeric value representing the vulnerability's age.<br>- The `interval` criterion specifies the unit of measure of the vulnerability's age: `day`, `week`, `month`, or `year`.<br><br>Example: `operator: greater_than`, `value: 30`, `interval: day`. |
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,8 @@ To view group import history:
|
|||
|
||||
## Review results of the import
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429109) in GitLab 16.6 [with a flag](../../feature_flags.md) named `bulk_import_details_page`. Enabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429109) in GitLab 16.6 [with a flag](../../feature_flags.md) named `bulk_import_details_page`. Enabled by default.
|
||||
> - Feature flag `bulk_import_details_page` removed in GitLab 16.8.
|
||||
|
||||
To review the results of an import:
|
||||
|
||||
|
|
|
|||
|
|
@ -1922,6 +1922,12 @@ msgstr ""
|
|||
msgid "AI-generated summary"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgents|AI Agents"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgents|Create agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIPoweredSM|AI-powered features"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -303,27 +303,11 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do
|
|||
describe 'GET details' do
|
||||
subject(:request) { get :details }
|
||||
|
||||
context 'when bulk_import_details_page feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(bulk_import_details_page: true)
|
||||
request
|
||||
end
|
||||
it 'responds with a 200 and shows the template', :aggregate_failures do
|
||||
request
|
||||
|
||||
it 'responds with a 200 and shows the template', :aggregate_failures do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template(:details)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when bulk_import_details_page feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(bulk_import_details_page: false)
|
||||
request
|
||||
end
|
||||
|
||||
it 'responds with a 404' do
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template(:details)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::Packages::InfrastructureRegistryController do
|
||||
RSpec.describe Projects::Packages::InfrastructureRegistryController, feature_category: :package_registry do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :private) }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::Packages::PackagesController do
|
||||
RSpec.describe Projects::Packages::PackagesController, feature_category: :package_registry do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
let(:page) { :index }
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ describe('Group import status component', () => {
|
|||
id: 2,
|
||||
entityId: 11,
|
||||
hasFailures: true,
|
||||
showDetailsLink: true,
|
||||
status: STATUSES.FINISHED,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { GlEmptyState, GlLoadingIcon, GlTableLite } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
|
||||
import BulkImportsHistoryApp from '~/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue';
|
||||
import ImportStatus from '~/import_entities/import_groups/components/import_status.vue';
|
||||
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ describe('BulkImportsHistoryApp', () => {
|
|||
destination_slug: 'top-level-group-12',
|
||||
destination_namespace: 'h5bp',
|
||||
created_at: '2021-07-08T10:03:44.743Z',
|
||||
has_failures: false,
|
||||
failures: [],
|
||||
},
|
||||
{
|
||||
|
|
@ -56,6 +57,7 @@ describe('BulkImportsHistoryApp', () => {
|
|||
project_id: null,
|
||||
created_at: '2021-07-13T12:52:26.664Z',
|
||||
updated_at: '2021-07-13T13:34:49.403Z',
|
||||
has_failures: true,
|
||||
failures: [
|
||||
{
|
||||
pipeline_class: 'BulkImports::Groups::Pipelines::GroupPipeline',
|
||||
|
|
@ -72,15 +74,19 @@ describe('BulkImportsHistoryApp', () => {
|
|||
let mock;
|
||||
const mockRealtimeChangesPath = '/import/realtime_changes.json';
|
||||
|
||||
function createComponent({ shallow = true } = {}) {
|
||||
function createComponent({ shallow = true, provide } = {}) {
|
||||
const mountFn = shallow ? shallowMount : mount;
|
||||
wrapper = mountFn(BulkImportsHistoryApp, {
|
||||
provide: { realtimeChangesPath: mockRealtimeChangesPath },
|
||||
provide: {
|
||||
realtimeChangesPath: mockRealtimeChangesPath,
|
||||
...provide,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
|
||||
const findPaginationBar = () => wrapper.findComponent(PaginationBar);
|
||||
const findImportStatusAt = (index) => wrapper.findAllComponents(ImportStatus).at(index);
|
||||
|
||||
beforeEach(() => {
|
||||
gon.api_version = 'v4';
|
||||
|
|
@ -201,77 +207,59 @@ describe('BulkImportsHistoryApp', () => {
|
|||
expect(findLocalStorageSync().props('value')).toBe(NEW_PAGE_SIZE);
|
||||
});
|
||||
|
||||
it('renders link to destination_full_path for destination group', async () => {
|
||||
createComponent({ shallow: false });
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.find('tbody tr a').attributes().href).toBe(
|
||||
`/${DUMMY_RESPONSE[0].destination_full_path}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders destination as text when destination_full_path is not defined', async () => {
|
||||
const RESPONSE = [{ ...DUMMY_RESPONSE[0], destination_full_path: null }];
|
||||
|
||||
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS);
|
||||
createComponent({ shallow: false });
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.find('tbody tr a').exists()).toBe(false);
|
||||
expect(wrapper.find('tbody tr span').text()).toBe(
|
||||
`${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_slug}/`,
|
||||
);
|
||||
});
|
||||
|
||||
it('adds slash to group urls', async () => {
|
||||
createComponent({ shallow: false });
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.find('tbody tr a').text()).toBe(`${DUMMY_RESPONSE[0].destination_full_path}/`);
|
||||
});
|
||||
|
||||
it('does not prefixes project urls with slash', async () => {
|
||||
createComponent({ shallow: false });
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.findAll('tbody tr a').at(1).text()).toBe(
|
||||
DUMMY_RESPONSE[1].destination_full_path,
|
||||
);
|
||||
});
|
||||
|
||||
describe('details button', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, DUMMY_RESPONSE, DEFAULT_HEADERS);
|
||||
describe('table rendering', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ shallow: false });
|
||||
return waitForPromises();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('renders details button if relevant item has failures', () => {
|
||||
expect(
|
||||
extendedWrapper(wrapper.find('tbody').findAll('tr').at(1)).findByText('Details').exists(),
|
||||
).toBe(true);
|
||||
it('renders link to destination_full_path for destination group', () => {
|
||||
expect(wrapper.find('tbody tr a').attributes().href).toBe(
|
||||
`/${DUMMY_RESPONSE[0].destination_full_path}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render details button if relevant item has no failures', () => {
|
||||
expect(
|
||||
extendedWrapper(wrapper.find('tbody').findAll('tr').at(0)).findByText('Details').exists(),
|
||||
).toBe(false);
|
||||
it('renders destination as text when destination_full_path is not defined', async () => {
|
||||
const RESPONSE = [{ ...DUMMY_RESPONSE[0], destination_full_path: null }];
|
||||
|
||||
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS);
|
||||
createComponent({ shallow: false });
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.find('tbody tr a').exists()).toBe(false);
|
||||
expect(wrapper.find('tbody tr span').text()).toBe(
|
||||
`${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_slug}/`,
|
||||
);
|
||||
});
|
||||
|
||||
it('expands details when details button is clicked', async () => {
|
||||
const ORIGINAL_ROW_INDEX = 1;
|
||||
await extendedWrapper(wrapper.find('tbody').findAll('tr').at(ORIGINAL_ROW_INDEX))
|
||||
.findByText('Details')
|
||||
.trigger('click');
|
||||
it('adds slash to group urls', () => {
|
||||
expect(wrapper.find('tbody tr a').text()).toBe(`${DUMMY_RESPONSE[0].destination_full_path}/`);
|
||||
});
|
||||
|
||||
const detailsRowContent = wrapper
|
||||
.find('tbody')
|
||||
.findAll('tr')
|
||||
.at(ORIGINAL_ROW_INDEX + 1)
|
||||
.find('pre');
|
||||
it('does not prefix project urls with slash', () => {
|
||||
expect(wrapper.findAll('tbody tr a').at(1).text()).toBe(
|
||||
DUMMY_RESPONSE[1].destination_full_path,
|
||||
);
|
||||
});
|
||||
|
||||
expect(detailsRowContent.exists()).toBe(true);
|
||||
expect(JSON.parse(detailsRowContent.text())).toStrictEqual(DUMMY_RESPONSE[1].failures);
|
||||
it('renders finished import status', () => {
|
||||
expect(findImportStatusAt(0).text()).toBe('Complete');
|
||||
});
|
||||
|
||||
it('renders failed import status with details link', async () => {
|
||||
createComponent({
|
||||
shallow: false,
|
||||
provide: {
|
||||
detailsPath: '/mock-details',
|
||||
},
|
||||
});
|
||||
await waitForPromises();
|
||||
|
||||
const failedImportStatus = findImportStatusAt(1);
|
||||
const failedImportStatusLink = failedImportStatus.find('a');
|
||||
expect(failedImportStatus.text()).toContain('Failed');
|
||||
expect(failedImportStatusLink.text()).toBe('See failures');
|
||||
expect(failedImportStatusLink.attributes('href')).toContain('/mock-details');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ describe('performance bar app', () => {
|
|||
statsUrl: 'https://log.gprd.gitlab.net/app/dashboards#/view/',
|
||||
peekUrl: '/-/peek/results',
|
||||
},
|
||||
stubs: {
|
||||
GlEmoji: { template: '<div/>' },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import RequestWarning from '~/performance_bar/components/request_warning.vue';
|
||||
|
||||
Vue.config.ignoredElements = ['gl-emoji'];
|
||||
|
|
@ -8,9 +8,20 @@ describe('request warning', () => {
|
|||
let wrapper;
|
||||
const htmlId = 'request-123';
|
||||
|
||||
const createComponent = ({ propsData = {} } = {}) => {
|
||||
wrapper = shallowMountExtended(RequestWarning, {
|
||||
propsData,
|
||||
stubs: {
|
||||
GlEmoji: { template: `<div id="${htmlId}" />` },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findEmoji = () => wrapper.findByTestId('warning');
|
||||
|
||||
describe('when the request has warnings', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(RequestWarning, {
|
||||
createComponent({
|
||||
propsData: {
|
||||
htmlId,
|
||||
warnings: ['gitaly calls: 30 over 10', 'gitaly duration: 1500 over 1000'],
|
||||
|
|
@ -19,14 +30,14 @@ describe('request warning', () => {
|
|||
});
|
||||
|
||||
it('adds a warning emoji with the correct ID', () => {
|
||||
expect(wrapper.find('span gl-emoji[id]').attributes('id')).toEqual(htmlId);
|
||||
expect(wrapper.find('span gl-emoji[id]').element.dataset.name).toEqual('warning');
|
||||
expect(findEmoji().attributes('id')).toEqual(htmlId);
|
||||
expect(findEmoji().element.dataset.name).toEqual('warning');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the request does not have warnings', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(RequestWarning, {
|
||||
createComponent({
|
||||
propsData: {
|
||||
htmlId,
|
||||
warnings: [],
|
||||
|
|
@ -35,7 +46,7 @@ describe('request warning', () => {
|
|||
});
|
||||
|
||||
it('does nothing', () => {
|
||||
expect(wrapper.html()).toBe('');
|
||||
expect(findEmoji().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ describe('CreateMenu component', () => {
|
|||
stubs: {
|
||||
InviteMembersTrigger,
|
||||
GlDisclosureDropdown,
|
||||
GlEmoji: { template: '<div/>' },
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: createMockDirective('gl-tooltip'),
|
||||
|
|
|
|||
|
|
@ -103,6 +103,9 @@ describe('WorkItemAssignees component', () => {
|
|||
},
|
||||
attachTo: document.body,
|
||||
apolloProvider,
|
||||
stubs: {
|
||||
GlEmoji: { template: '<div/>' },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue