Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-02 21:11:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-02 21:11:20 +0300
commitbf2439c21308c74e437b872180046b39a61734b5 (patch)
treed6f1fe33eb4b726e4e9f11350b8838dae2fc53f4 /spec
parent426384d091a0c229ff849ed6ba481bfbe700fb6a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/alerts_settings/user_views_alerts_settings_spec.rb6
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report.json16
-rw-r--r--spec/frontend/alerts_settings/components/__snapshots__/alerts_settings_form_spec.js.snap680
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_form_spec.js79
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js156
-rw-r--r--spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap1
-rw-r--r--spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js2
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js4
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js9
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js4
-rw-r--r--spec/frontend/reports/components/grouped_test_reports_app_spec.js2
-rw-r--r--spec/frontend/reports/components/summary_row_spec.js24
-rw-r--r--spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js2
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js14
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js48
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb10
-rw-r--r--spec/lib/gitlab/graphql/docs/renderer_spec.rb10
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb2
-rw-r--r--spec/services/ci/register_job_service_spec.rb40
-rw-r--r--spec/services/system_notes/merge_requests_service_spec.rb2
20 files changed, 702 insertions, 409 deletions
diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
index 07c87f98eb6..e4c39276418 100644
--- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
+++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
@@ -30,8 +30,8 @@ RSpec.describe 'Alert integrations settings form', :js do
end
end
- it 'shows the new alerts setting form' do
- expect(page).to have_content('1. Select integration type')
+ it 'shows the integrations list title' do
+ expect(page).to have_content('Current integrations')
end
end
end
@@ -44,7 +44,7 @@ RSpec.describe 'Alert integrations settings form', :js do
wait_for_requests
end
- it 'shows the old alerts setting form' do
+ it 'does not have rights to access the setting form' do
expect(page).not_to have_selector('.incident-management-list')
expect(page).not_to have_selector('#js-alert-management-settings')
end
diff --git a/spec/fixtures/security_reports/master/gl-sast-report.json b/spec/fixtures/security_reports/master/gl-sast-report.json
index 98bb15e349f..ab610945508 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report.json
@@ -28,7 +28,21 @@
"file": "python/hardcoded/hardcoded-tmp.py",
"line": 1,
"url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html",
- "tool": "bandit"
+ "tool": "bandit",
+ "tracking": {
+ "type": "source",
+ "items": [
+ {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 1,
+ "end_line": 1,
+ "fingerprints": [
+ { "algorithm": "hash", "value": "HASHVALUE" },
+ { "algorithm": "scope_offset", "value": "python/hardcoded/hardcoded-tmp.py:ClassA:method_b:2" }
+ ]
+ }
+ ]
+ }
},
{
"category": "sast",
diff --git a/spec/frontend/alerts_settings/components/__snapshots__/alerts_settings_form_spec.js.snap b/spec/frontend/alerts_settings/components/__snapshots__/alerts_settings_form_spec.js.snap
index eb2b82a0211..ef6a7621b51 100644
--- a/spec/frontend/alerts_settings/components/__snapshots__/alerts_settings_form_spec.js.snap
+++ b/spec/frontend/alerts_settings/components/__snapshots__/alerts_settings_form_spec.js.snap
@@ -4,113 +4,152 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
<form
class="gl-mt-6"
>
- <h5
- class="gl-font-lg gl-my-5"
- >
- Add new integrations
- </h5>
-
<div
- class="form-group gl-form-group"
- id="integration-type"
- role="group"
+ class="tabs gl-tabs"
+ id="__BVID__6"
>
- <label
- class="d-block col-form-label"
- for="integration-type"
- id="integration-type__BV_label_"
- >
- 1. Select integration type
- </label>
+ <!---->
<div
- class="bv-no-focus-ring"
+ class=""
>
- <select
- class="gl-form-select mw-100 custom-select"
- id="__BVID__8"
+ <ul
+ class="nav gl-tabs-nav"
+ id="__BVID__6__BV_tab_controls_"
+ role="tablist"
>
- <option
- value=""
+ <!---->
+ <li
+ class="nav-item"
+ role="presentation"
>
- Select integration type
- </option>
- <option
- value="HTTP"
+ <a
+ aria-controls="__BVID__8"
+ aria-posinset="1"
+ aria-selected="true"
+ aria-setsize="3"
+ class="nav-link active gl-tab-nav-item gl-tab-nav-item-active gl-tab-nav-item-active-indigo"
+ href="#"
+ id="__BVID__8___BV_tab_button__"
+ role="tab"
+ target="_self"
+ >
+ Configure details
+ </a>
+ </li>
+ <li
+ class="nav-item"
+ role="presentation"
>
- HTTP Endpoint
- </option>
- <option
- value="PROMETHEUS"
+ <a
+ aria-controls="__BVID__22"
+ aria-disabled="true"
+ aria-posinset="2"
+ aria-selected="false"
+ aria-setsize="3"
+ class="nav-link disabled disabled gl-tab-nav-item"
+ href="#"
+ id="__BVID__22___BV_tab_button__"
+ role="tab"
+ tabindex="-1"
+ target="_self"
+ >
+ View credentials
+ </a>
+ </li>
+ <li
+ class="nav-item"
+ role="presentation"
>
- External Prometheus
- </option>
- </select>
-
- <!---->
- <!---->
- <!---->
- <!---->
+ <a
+ aria-controls="__BVID__41"
+ aria-disabled="true"
+ aria-posinset="3"
+ aria-selected="false"
+ aria-setsize="3"
+ class="nav-link disabled disabled gl-tab-nav-item"
+ href="#"
+ id="__BVID__41___BV_tab_button__"
+ role="tab"
+ tabindex="-1"
+ target="_self"
+ >
+ Send test alert
+ </a>
+ </li>
+ <!---->
+ </ul>
</div>
- </div>
-
- <transition-stub
- class="gl-mt-3"
- css="true"
- enteractiveclass="collapsing"
- enterclass=""
- entertoclass="collapse show"
- leaveactiveclass="collapsing"
- leaveclass="collapse show"
- leavetoclass="collapse"
- >
<div
- class="collapse"
- id="__BVID__10"
- style="display: none;"
+ class="tab-content gl-tab-content"
+ id="__BVID__6__BV_tab_container_"
>
- <div>
+ <transition-stub
+ css="true"
+ enteractiveclass=""
+ enterclass=""
+ entertoclass="show"
+ leaveactiveclass=""
+ leaveclass="show"
+ leavetoclass=""
+ mode="out-in"
+ name=""
+ >
<div
- class="form-group gl-form-group"
- id="name-integration"
- role="group"
+ aria-hidden="false"
+ aria-labelledby="__BVID__8___BV_tab_button__"
+ class="tab-pane active"
+ id="__BVID__8"
+ role="tabpanel"
+ style=""
>
- <label
- class="d-block col-form-label"
- for="name-integration"
- id="name-integration__BV_label_"
- >
- 2. Name integration
- </label>
<div
- class="bv-no-focus-ring"
+ class="form-group gl-form-group"
+ id="integration-type"
+ role="group"
>
- <input
- class="gl-form-input form-control"
- id="__BVID__15"
- placeholder="Enter integration name"
- type="text"
- />
- <!---->
- <!---->
- <!---->
+ <label
+ class="d-block col-form-label"
+ for="integration-type"
+ id="integration-type__BV_label_"
+ >
+ Select integration type
+ </label>
+ <div
+ class="bv-no-focus-ring"
+ >
+ <select
+ class="gl-form-select gl-max-w-full custom-select"
+ id="__BVID__13"
+ >
+ <option
+ value=""
+ >
+ Select integration type
+ </option>
+ <option
+ value="HTTP"
+ >
+ HTTP Endpoint
+ </option>
+ <option
+ value="PROMETHEUS"
+ >
+ External Prometheus
+ </option>
+ </select>
+
+ <!---->
+ <!---->
+ <!---->
+ <!---->
+ </div>
</div>
- </div>
-
- <div
- class="form-group gl-form-group"
- id="integration-webhook"
- role="group"
- >
- <label
- class="d-block col-form-label"
- for="integration-webhook"
- id="integration-webhook__BV_label_"
- >
- 3. Set up webhook
- </label>
+
<div
- class="bv-no-focus-ring"
+ class="gl-mt-3"
>
+ <!---->
+
<span>
Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the
<a
@@ -166,241 +205,324 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
<!---->
- <div
- class="gl-my-4"
+ <!---->
+
+ <!---->
+
+ <!---->
+ </div>
+
+ <div
+ class="gl-display-flex gl-justify-content-start gl-py-3"
+ >
+ <button
+ class="btn js-no-auto-disable btn-confirm btn-md gl-button"
+ data-testid="integration-form-submit"
+ type="submit"
>
+ <!---->
+
+ <!---->
+
<span
- class="gl-font-weight-bold"
+ class="gl-button-text"
>
- Webhook URL
-
+ Save integration
+
</span>
+ </button>
+
+ <button
+ class="btn gl-ml-3 js-no-auto-disable btn-default btn-md gl-button"
+ type="reset"
+ >
+ <!---->
+ <!---->
+
+ <span
+ class="gl-button-text"
+ >
+ Cancel and close
+ </span>
+ </button>
+ </div>
+ </div>
+ </transition-stub>
+
+ <transition-stub
+ css="true"
+ enteractiveclass=""
+ enterclass=""
+ entertoclass="show"
+ leaveactiveclass=""
+ leaveclass="show"
+ leavetoclass=""
+ mode="out-in"
+ name=""
+ >
+ <div
+ aria-hidden="true"
+ aria-labelledby="__BVID__22___BV_tab_button__"
+ class="tab-pane disabled"
+ id="__BVID__22"
+ role="tabpanel"
+ style="display: none;"
+ >
+ <fieldset
+ class="form-group gl-form-group"
+ id="integration-webhook"
+ >
+ <!---->
+ <div
+ class="bv-no-focus-ring"
+ role="group"
+ tabindex="-1"
+ >
<div
- id="url"
- readonly="readonly"
+ class="gl-my-4"
>
+ <span
+ class="gl-font-weight-bold"
+ >
+
+ Webhook URL
+
+ </span>
+
<div
- class="input-group"
- role="group"
+ id="url"
+ readonly="readonly"
>
- <!---->
- <!---->
-
- <input
- class="gl-form-input form-control"
- id="url"
- readonly="readonly"
- type="text"
- />
-
<div
- class="input-group-append"
+ class="input-group"
+ role="group"
>
- <button
- aria-label="Copy this value"
- class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
- data-clipboard-text=""
- title="Copy"
- type="button"
+ <!---->
+ <!---->
+
+ <input
+ class="gl-form-input form-control"
+ id="url"
+ readonly="readonly"
+ type="text"
+ />
+
+ <div
+ class="input-group-append"
>
- <!---->
-
- <svg
- aria-hidden="true"
- class="gl-button-icon gl-icon s16"
- data-testid="copy-to-clipboard-icon"
+ <button
+ aria-label="Copy this value"
+ class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
+ data-clipboard-text=""
+ title="Copy"
+ type="button"
>
- <use
- href="#copy-to-clipboard"
- />
- </svg>
-
- <!---->
- </button>
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="copy-to-clipboard-icon"
+ >
+ <use
+ href="#copy-to-clipboard"
+ />
+ </svg>
+
+ <!---->
+ </button>
+ </div>
+ <!---->
</div>
- <!---->
</div>
</div>
- </div>
-
- <div
- class="gl-my-4"
- >
- <span
- class="gl-font-weight-bold"
- >
-
- Authorization key
-
- </span>
<div
- class="gl-mb-3"
- id="authorization-key"
- readonly="readonly"
+ class="gl-my-4"
>
+ <span
+ class="gl-font-weight-bold"
+ >
+
+ Authorization key
+
+ </span>
+
<div
- class="input-group"
- role="group"
+ class="gl-mb-3"
+ id="authorization-key"
+ readonly="readonly"
>
- <!---->
- <!---->
-
- <input
- class="gl-form-input form-control"
- id="authorization-key"
- readonly="readonly"
- type="text"
- />
-
<div
- class="input-group-append"
+ class="input-group"
+ role="group"
>
- <button
- aria-label="Copy this value"
- class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
- data-clipboard-text=""
- title="Copy"
- type="button"
+ <!---->
+ <!---->
+
+ <input
+ class="gl-form-input form-control"
+ id="authorization-key"
+ readonly="readonly"
+ type="text"
+ />
+
+ <div
+ class="input-group-append"
>
- <!---->
-
- <svg
- aria-hidden="true"
- class="gl-button-icon gl-icon s16"
- data-testid="copy-to-clipboard-icon"
+ <button
+ aria-label="Copy this value"
+ class="btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon"
+ data-clipboard-text=""
+ title="Copy"
+ type="button"
>
- <use
- href="#copy-to-clipboard"
- />
- </svg>
-
- <!---->
- </button>
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="copy-to-clipboard-icon"
+ >
+ <use
+ href="#copy-to-clipboard"
+ />
+ </svg>
+
+ <!---->
+ </button>
+ </div>
+ <!---->
</div>
- <!---->
</div>
</div>
-
- <button
- class="btn btn-default btn-md disabled gl-button"
- disabled="disabled"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Reset Key
-
- </span>
- </button>
-
+ <!---->
+ <!---->
<!---->
</div>
- <!---->
- <!---->
- <!---->
- </div>
- </div>
-
- <div
- class="form-group gl-form-group"
- id="test-integration"
- role="group"
- >
- <label
- class="d-block col-form-label"
- for="test-integration"
- id="test-integration__BV_label_"
- >
- 4. Sample alert payload (optional)
- </label>
- <div
- class="bv-no-focus-ring"
+ </fieldset>
+
+ <button
+ class="btn btn-danger btn-md disabled gl-button"
+ disabled="disabled"
+ type="button"
>
- <span>
- Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).
- </span>
+ <!---->
- <textarea
- class="gl-form-input gl-form-textarea gl-my-3 form-control is-valid"
- disabled="disabled"
- id="test-payload"
- placeholder="{ \\"events\\": [{ \\"application\\": \\"Name of application\\" }] }"
- style="resize: none; overflow-y: scroll;"
- wrap="soft"
- />
<!---->
+
+ <span
+ class="gl-button-text"
+ >
+
+ Reset Key
+
+ </span>
+ </button>
+
+ <button
+ class="btn gl-ml-3 js-no-auto-disable btn-default btn-md gl-button"
+ type="reset"
+ >
<!---->
+
<!---->
- </div>
+
+ <span
+ class="gl-button-text"
+ >
+ Cancel and close
+ </span>
+ </button>
+
+ <!---->
</div>
-
- <!---->
-
- <!---->
- </div>
+ </transition-stub>
- <div
- class="gl-display-flex gl-justify-content-start gl-py-3"
+ <transition-stub
+ css="true"
+ enteractiveclass=""
+ enterclass=""
+ entertoclass="show"
+ leaveactiveclass=""
+ leaveclass="show"
+ leavetoclass=""
+ mode="out-in"
+ name=""
>
- <button
- class="btn js-no-auto-disable btn-success btn-md gl-button"
- data-testid="integration-form-submit"
- type="submit"
+ <div
+ aria-hidden="true"
+ aria-labelledby="__BVID__41___BV_tab_button__"
+ class="tab-pane disabled"
+ id="__BVID__41"
+ role="tabpanel"
+ style="display: none;"
>
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
+ <fieldset
+ class="form-group gl-form-group"
+ id="test-integration"
>
- Save integration
-
- </span>
- </button>
-
- <button
- class="btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary"
- data-testid="integration-test-and-submit"
- disabled="disabled"
- type="button"
- >
- <!---->
+ <!---->
+ <div
+ class="bv-no-focus-ring"
+ role="group"
+ tabindex="-1"
+ >
+ <span>
+ Provide an example payload from the monitoring tool you intend to integrate with. This will allow you to send an alert to an active GitLab alerting point.
+ </span>
+
+ <textarea
+ class="gl-form-input gl-form-textarea gl-my-3 form-control is-valid"
+ id="test-payload"
+ placeholder="{ \\"events\\": [{ \\"application\\": \\"Name of application\\" }] }"
+ style="resize: none; overflow-y: scroll;"
+ wrap="soft"
+ />
+ <!---->
+ <!---->
+ <!---->
+ </div>
+ </fieldset>
- <!---->
-
- <span
- class="gl-button-text"
+ <button
+ class="btn js-no-auto-disable btn-confirm btn-md gl-button"
+ data-testid="send-test-alert"
+ type="button"
>
- Save and test payload
- </span>
- </button>
-
- <button
- class="btn js-no-auto-disable btn-default btn-md gl-button"
- type="reset"
- >
- <!---->
+ <!---->
+
+ <!---->
+
+ <span
+ class="gl-button-text"
+ >
+
+ Send
+
+ </span>
+ </button>
- <!---->
-
- <span
- class="gl-button-text"
+ <button
+ class="btn gl-ml-3 js-no-auto-disable btn-default btn-md gl-button"
+ type="reset"
>
- Cancel
- </span>
- </button>
- </div>
+ <!---->
+
+ <!---->
+
+ <span
+ class="gl-button-text"
+ >
+ Cancel and close
+ </span>
+ </button>
+ </div>
+ </transition-stub>
+ <!---->
</div>
- </transition-stub>
+ </div>
</form>
`;
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
index 511b3d2a059..f8e192dfae9 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
@@ -1,11 +1,4 @@
-import {
- GlForm,
- GlFormSelect,
- GlCollapse,
- GlFormInput,
- GlToggle,
- GlFormTextarea,
-} from '@gitlab/ui';
+import { GlForm, GlFormSelect, GlFormInput, GlToggle, GlFormTextarea, GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import MappingBuilder from '~/alerts_settings/components/alert_mapping_builder.vue';
@@ -52,18 +45,18 @@ describe('AlertsSettingsForm', () => {
const findForm = () => wrapper.find(GlForm);
const findSelect = () => wrapper.find(GlFormSelect);
- const findFormSteps = () => wrapper.find(GlCollapse);
const findFormFields = () => wrapper.findAll(GlFormInput);
const findFormToggle = () => wrapper.find(GlToggle);
- const findTestPayloadSection = () => wrapper.find(`[id = "test-integration"]`);
+ const findSamplePayloadSection = () => wrapper.find('[data-testid="sample-payload-section"]');
const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
const findMappingBuilder = () => wrapper.findComponent(MappingBuilder);
const findSubmitButton = () => wrapper.find(`[type = "submit"]`);
const findMultiSupportText = () =>
wrapper.find(`[data-testid="multi-integrations-not-supported"]`);
- const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`);
+ const findJsonTestSubmit = () => wrapper.find(`[data-testid="send-test-alert"]`);
const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`);
const findActionBtn = () => wrapper.find(`[data-testid="payload-action-btn"]`);
+ const findTabs = () => wrapper.findAll(GlTab);
afterEach(() => {
if (wrapper) {
@@ -95,7 +88,7 @@ describe('AlertsSettingsForm', () => {
expect(findForm().exists()).toBe(true);
expect(findSelect().exists()).toBe(true);
expect(findMultiSupportText().exists()).toBe(false);
- expect(findFormSteps().attributes('visible')).toBeUndefined();
+ expect(findFormFields()).toHaveLength(0);
});
it('shows the rest of the form when the dropdown is used', async () => {
@@ -110,11 +103,40 @@ describe('AlertsSettingsForm', () => {
expect(findMultiSupportText().exists()).toBe(true);
});
- it('disabled the name input when the selected value is prometheus', async () => {
+ it('hides the name input when the selected value is prometheus', async () => {
createComponent();
await selectOptionAtIndex(2);
+ expect(findFormFields().at(0).attributes('id')).not.toBe('name-integration');
+ });
+
+ describe('form tabs', () => {
+ it('renders 3 tabs', () => {
+ expect(findTabs()).toHaveLength(3);
+ });
- expect(findFormFields().at(0).attributes('disabled')).toBe('disabled');
+ it('only first tab is enabled on integration create', () => {
+ createComponent({
+ data: {
+ currentIntegration: null,
+ },
+ });
+ const tabs = findTabs();
+ expect(tabs.at(0).find('[role="tabpanel"]').classes('disabled')).toBe(false);
+ expect(tabs.at(1).find('[role="tabpanel"]').classes('disabled')).toBe(true);
+ expect(tabs.at(2).find('[role="tabpanel"]').classes('disabled')).toBe(true);
+ });
+
+ it('all tabs are enabled on integration edit', () => {
+ createComponent({
+ data: {
+ currentIntegration: { id: 1 },
+ },
+ });
+ const tabs = findTabs();
+ expect(tabs.at(0).find('[role="tabpanel"]').classes('disabled')).toBe(false);
+ expect(tabs.at(1).find('[role="tabpanel"]').classes('disabled')).toBe(false);
+ expect(tabs.at(2).find('[role="tabpanel"]').classes('disabled')).toBe(false);
+ });
});
});
@@ -195,14 +217,9 @@ describe('AlertsSettingsForm', () => {
describe('PROMETHEUS', () => {
it('create', async () => {
createComponent();
-
await selectOptionAtIndex(2);
-
const apiUrl = 'https://test.com';
- enableIntegration(1, apiUrl);
-
- findFormToggle().trigger('click');
-
+ enableIntegration(0, apiUrl);
const submitBtn = findSubmitButton();
expect(submitBtn.exists()).toBe(true);
expect(submitBtn.text()).toBe('Save integration');
@@ -226,7 +243,7 @@ describe('AlertsSettingsForm', () => {
});
const apiUrl = 'https://test-post.com';
- enableIntegration(1, apiUrl);
+ enableIntegration(0, apiUrl);
const submitBtn = findSubmitButton();
expect(submitBtn.exists()).toBe(true);
@@ -264,7 +281,7 @@ describe('AlertsSettingsForm', () => {
const jsonTestSubmit = findJsonTestSubmit();
expect(jsonTestSubmit.exists()).toBe(true);
- expect(jsonTestSubmit.text()).toBe('Save and test payload');
+ expect(jsonTestSubmit.text()).toBe('Send');
expect(jsonTestSubmit.props('disabled')).toBe(true);
});
@@ -313,16 +330,14 @@ describe('AlertsSettingsForm', () => {
it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => {
wrapper.setData({
- currentIntegration: {
- type: typeSet.http,
- payloadExample: validSamplePayload,
- payloadAttributeMappings: [],
- },
+ selectedIntegration: typeSet.http,
active,
resetPayloadAndMappingConfirmed,
});
await wrapper.vm.$nextTick();
- expect(findTestPayloadSection().find(GlFormTextarea).attributes('disabled')).toBe(disabled);
+ expect(findSamplePayloadSection().find(GlFormTextarea).attributes('disabled')).toBe(
+ disabled,
+ );
});
});
@@ -330,9 +345,9 @@ describe('AlertsSettingsForm', () => {
describe.each`
resetPayloadAndMappingConfirmed | payloadExample | caption
${false} | ${validSamplePayload} | ${'Edit payload'}
- ${true} | ${emptySamplePayload} | ${'Submit payload'}
- ${true} | ${validSamplePayload} | ${'Submit payload'}
- ${false} | ${emptySamplePayload} | ${'Submit payload'}
+ ${true} | ${emptySamplePayload} | ${'Parse payload for custom mapping'}
+ ${true} | ${validSamplePayload} | ${'Parse payload for custom mapping'}
+ ${false} | ${emptySamplePayload} | ${'Parse payload for custom mapping'}
`('', ({ resetPayloadAndMappingConfirmed, payloadExample, caption }) => {
const samplePayloadMsg = payloadExample ? 'was provided' : 'was not provided';
const payloadResetMsg = resetPayloadAndMappingConfirmed
@@ -386,7 +401,7 @@ describe('AlertsSettingsForm', () => {
await waitForPromises();
- expect(findTestPayloadSection().find('.invalid-feedback').text()).toBe(errorMessage);
+ expect(findSamplePayloadSection().find('.invalid-feedback').text()).toBe(errorMessage);
});
});
});
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index 8e63b24284b..77fac6dd022 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -9,7 +9,9 @@ import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import waitForPromises from 'helpers/wait_for_promises';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
-import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
+import AlertsSettingsWrapper, {
+ i18n,
+} from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import { typeSet } from '~/alerts_settings/constants';
import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql';
import destroyHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql';
@@ -19,6 +21,7 @@ import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/muta
import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
+import alertsUpdateService from '~/alerts_settings/services';
import {
ADD_INTEGRATION_ERROR,
RESET_INTEGRATION_TOKEN_ERROR,
@@ -26,7 +29,7 @@ import {
INTEGRATION_PAYLOAD_TEST_ERROR,
DELETE_INTEGRATION_ERROR,
} from '~/alerts_settings/utils/error_messages';
-import createFlash from '~/flash';
+import createFlash, { FLASH_TYPES } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
createHttpVariables,
@@ -78,6 +81,8 @@ describe('AlertsSettingsWrapper', () => {
const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon);
const findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
+ const findAddIntegrationBtn = () => wrapper.find('[data-testid="add-integration-btn"]');
+ const findAlertsSettingsForm = () => wrapper.findComponent(AlertsSettingsForm);
async function destroyHttpIntegration(localWrapper) {
await jest.runOnlyPendingTimers();
@@ -144,14 +149,37 @@ describe('AlertsSettingsWrapper', () => {
wrapper = null;
});
- describe('rendered via default permissions', () => {
- it('renders the GraphQL alerts integrations list and new form', () => {
- createComponent();
- expect(wrapper.find(IntegrationsList).exists()).toBe(true);
- expect(wrapper.find(AlertsSettingsForm).exists()).toBe(true);
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent({
+ data: {
+ integrations: { list: mockIntegrations },
+ httpIntegrations: { list: [] },
+ currentIntegration: mockIntegrations[0],
+ },
+ loading: false,
+ });
});
- it('uses a loading state inside the IntegrationsList table', () => {
+ it('renders alerts integrations list and add new integration button by default', () => {
+ expect(findLoader().exists()).toBe(false);
+ expect(findIntegrations()).toHaveLength(mockIntegrations.length);
+ expect(findAddIntegrationBtn().exists()).toBe(true);
+ });
+
+ it('does NOT render settings form by default', () => {
+ expect(findAlertsSettingsForm().exists()).toBe(false);
+ });
+
+ it('hides `add new integration` button and displays setting form on btn click', async () => {
+ const addNewIntegrationBtn = findAddIntegrationBtn();
+ expect(addNewIntegrationBtn.exists()).toBe(true);
+ await addNewIntegrationBtn.trigger('click');
+ expect(findAlertsSettingsForm().exists()).toBe(true);
+ expect(addNewIntegrationBtn.exists()).toBe(false);
+ });
+
+ it('shows loading indicator inside the IntegrationsList table', () => {
createComponent({
data: { integrations: {} },
loading: true,
@@ -159,26 +187,24 @@ describe('AlertsSettingsWrapper', () => {
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(findLoader().exists()).toBe(true);
});
+ });
- it('renders the IntegrationsList table using the API data', () => {
+ describe('Integration updates', () => {
+ beforeEach(() => {
createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
+ data: {
+ integrations: { list: mockIntegrations },
+ currentIntegration: mockIntegrations[0],
+ formVisible: true,
+ },
loading: false,
});
- expect(findLoader().exists()).toBe(false);
- expect(findIntegrations()).toHaveLength(mockIntegrations.length);
});
-
it('calls `$apollo.mutate` with `createHttpIntegrationMutation`', () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createHttpIntegrationMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {
+ findAlertsSettingsForm().vm.$emit('create-new-integration', {
type: typeSet.http,
variables: createHttpVariables,
});
@@ -192,15 +218,10 @@ describe('AlertsSettingsWrapper', () => {
});
it('calls `$apollo.mutate` with `updateHttpIntegrationMutation`', () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updateHttpIntegrationMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {
+ findAlertsSettingsForm().vm.$emit('update-integration', {
type: typeSet.http,
variables: updateHttpVariables,
});
@@ -212,15 +233,10 @@ describe('AlertsSettingsWrapper', () => {
});
it('calls `$apollo.mutate` with `resetHttpTokenMutation`', () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetHttpTokenMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
+ findAlertsSettingsForm().vm.$emit('reset-token', {
type: typeSet.http,
variables: { id: HTTP_ID },
});
@@ -234,15 +250,10 @@ describe('AlertsSettingsWrapper', () => {
});
it('calls `$apollo.mutate` with `createPrometheusIntegrationMutation`', () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createPrometheusIntegrationMutation: { integration: { id: '2' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {
+ findAlertsSettingsForm().vm.$emit('create-new-integration', {
type: typeSet.prometheus,
variables: createPrometheusVariables,
});
@@ -257,14 +268,18 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[3] },
+ data: {
+ integrations: { list: mockIntegrations },
+ currentIntegration: mockIntegrations[3],
+ formVisible: true,
+ },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updatePrometheusIntegrationMutation: { integration: { id: '2' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {
+ findAlertsSettingsForm().vm.$emit('update-integration', {
type: typeSet.prometheus,
variables: updatePrometheusVariables,
});
@@ -276,15 +291,10 @@ describe('AlertsSettingsWrapper', () => {
});
it('calls `$apollo.mutate` with `resetPrometheusTokenMutation`', () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetPrometheusTokenMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
+ findAlertsSettingsForm().vm.$emit('reset-token', {
type: typeSet.prometheus,
variables: { id: PROMETHEUS_ID },
});
@@ -298,13 +308,8 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows an error alert when integration creation fails ', async () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(ADD_INTEGRATION_ERROR);
- wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {});
+ findAlertsSettingsForm().vm.$emit('create-new-integration', {});
await waitForPromises();
@@ -312,28 +317,18 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows an error alert when integration token reset fails ', async () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(RESET_INTEGRATION_TOKEN_ERROR);
- wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {});
+ findAlertsSettingsForm().vm.$emit('reset-token', {});
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: RESET_INTEGRATION_TOKEN_ERROR });
});
it('shows an error alert when integration update fails ', async () => {
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
- wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {});
+ findAlertsSettingsForm().vm.$emit('update-integration', {});
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR });
@@ -342,12 +337,7 @@ describe('AlertsSettingsWrapper', () => {
it('shows an error alert when integration test payload fails ', async () => {
const mock = new AxiosMockAdapter(axios);
mock.onPost(/(.*)/).replyOnce(403);
- createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- loading: false,
- });
-
- return wrapper.vm.validateAlertPayload({ endpoint: '', data: '', token: '' }).then(() => {
+ return wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' }).then(() => {
expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
expect(createFlash).toHaveBeenCalledTimes(1);
mock.restore();
@@ -389,6 +379,34 @@ describe('AlertsSettingsWrapper', () => {
variables: mockIntegrations[3],
});
});
+
+ describe('Test alert', () => {
+ it('makes `updateTestAlert` service call', async () => {
+ jest.spyOn(alertsUpdateService, 'updateTestAlert').mockResolvedValueOnce();
+ const testPayload = '{"title":"test"}';
+ findAlertsSettingsForm().vm.$emit('test-alert-payload', testPayload);
+ expect(alertsUpdateService.updateTestAlert).toHaveBeenCalledWith(testPayload);
+ });
+
+ it('shows success message on successful test', async () => {
+ jest.spyOn(alertsUpdateService, 'updateTestAlert').mockResolvedValueOnce({});
+ findAlertsSettingsForm().vm.$emit('test-alert-payload', '');
+ await waitForPromises();
+ expect(createFlash).toHaveBeenCalledWith({
+ message: i18n.alertSent,
+ type: FLASH_TYPES.SUCCESS,
+ });
+ });
+
+ it('shows error message when test alert fails', async () => {
+ jest.spyOn(alertsUpdateService, 'updateTestAlert').mockRejectedValueOnce({});
+ findAlertsSettingsForm().vm.$emit('test-alert-payload', '');
+ await waitForPromises();
+ expect(createFlash).toHaveBeenCalledWith({
+ message: INTEGRATION_PAYLOAD_TEST_ERROR,
+ });
+ });
+ });
});
describe('with mocked Apollo client', () => {
diff --git a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
index bf33aa731ef..2691e11e616 100644
--- a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
+++ b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
@@ -7,7 +7,6 @@ exports[`Keep latest artifact checkbox when application keep latest artifact set
<b-form-checkbox-stub
checked="true"
class="gl-form-checkbox"
- plain="true"
value="true"
>
<strong
diff --git a/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js b/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
index 9672f6a315a..51b4106d4b1 100644
--- a/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
+++ b/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
@@ -23,7 +23,7 @@ describe('DuplicateDashboardForm', () => {
findByRef(ref).setValue(val);
};
const setChecked = (value) => {
- const input = wrapper.find(`.form-check-input[value="${value}"]`);
+ const input = wrapper.find(`.custom-control-input[value="${value}"]`);
input.element.checked = true;
input.trigger('click');
input.trigger('change');
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
index 3e8d4ba314c..6c3f848333c 100644
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_spec.js
@@ -20,6 +20,10 @@ describe('graph component', () => {
const defaultProps = {
pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
+ configPaths: {
+ metricsPath: '',
+ graphqlResourceEtag: 'this/is/a/path',
+ },
};
const defaultData = {
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 202365ecd35..b53969ab05c 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -9,6 +9,8 @@ import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_w
import { mockPipelineResponse } from './mock_data';
const defaultProvide = {
+ graphqlResourceEtag: 'frog/amphibirama/etag/',
+ metricsPath: '',
pipelineProjectPath: 'frog/amphibirama',
pipelineIid: '22',
};
@@ -87,6 +89,13 @@ describe('Pipeline graph wrapper', () => {
it('displays the graph', () => {
expect(getGraph().exists()).toBe(true);
});
+
+ it('passes the etag resource and metrics path to the graph', () => {
+ expect(getGraph().props('configPaths')).toMatchObject({
+ graphqlResourceEtag: defaultProvide.graphqlResourceEtag,
+ metricsPath: defaultProvide.metricsPath,
+ });
+ });
});
describe('when there is an error', () => {
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 8f01accccc1..edeab883e91 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -20,6 +20,10 @@ describe('Linked Pipelines Column', () => {
columnTitle: 'Downstream',
linkedPipelines: processedPipeline.downstream,
type: DOWNSTREAM,
+ configPaths: {
+ metricsPath: '',
+ graphqlResourceEtag: 'this/is/a/path',
+ },
};
let wrapper;
diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
index ed261ed12c0..693cdb5fccd 100644
--- a/spec/frontend/reports/components/grouped_test_reports_app_spec.js
+++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
@@ -42,7 +42,7 @@ describe('Grouped test reports app', () => {
const findHeader = () => wrapper.find('[data-testid="report-section-code-text"]');
const findExpandButton = () => wrapper.find('[data-testid="report-section-expand-button"]');
const findFullTestReportLink = () => wrapper.find('[data-testid="group-test-reports-full-link"]');
- const findSummaryDescription = () => wrapper.find('[data-testid="test-summary-row-description"]');
+ const findSummaryDescription = () => wrapper.find('[data-testid="summary-row-description"]');
const findIssueDescription = () => wrapper.find('[data-testid="test-issue-body-description"]');
const findAllIssueDescriptions = () =>
wrapper.findAll('[data-testid="test-issue-body-description"]');
diff --git a/spec/frontend/reports/components/summary_row_spec.js b/spec/frontend/reports/components/summary_row_spec.js
index bdd6de1e0be..04d9d10dcd2 100644
--- a/spec/frontend/reports/components/summary_row_spec.js
+++ b/spec/frontend/reports/components/summary_row_spec.js
@@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SummaryRow from '~/reports/components/summary_row.vue';
describe('Summary row', () => {
@@ -14,16 +15,19 @@ describe('Summary row', () => {
};
const createComponent = ({ propsData = {}, slots = {} } = {}) => {
- wrapper = mount(SummaryRow, {
- propsData: {
- ...props,
- ...propsData,
- },
- slots,
- });
+ wrapper = extendedWrapper(
+ mount(SummaryRow, {
+ propsData: {
+ ...props,
+ ...propsData,
+ },
+ slots,
+ }),
+ );
};
- const findSummary = () => wrapper.find('.report-block-list-issue-description-text');
+ const findSummary = () => wrapper.findByTestId('summary-row-description');
+ const findStatusIcon = () => wrapper.findByTestId('summary-row-icon');
afterEach(() => {
wrapper.destroy();
@@ -37,9 +41,7 @@ describe('Summary row', () => {
it('renders provided icon', () => {
createComponent();
- expect(wrapper.find('.report-block-list-icon span').classes()).toContain(
- 'js-ci-status-icon-warning',
- );
+ expect(findStatusIcon().classes()).toContain('js-ci-status-icon-warning');
});
describe('summary slot', () => {
diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
index b2cc7d9be6b..e2386bc7f2b 100644
--- a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
+++ b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
@@ -48,7 +48,7 @@ describe('Merge Requests Artifacts list app', () => {
};
const findButtons = () => wrapper.findAll('button');
- const findTitle = () => wrapper.find('.js-title');
+ const findTitle = () => wrapper.find('[data-testid="mr-collapsible-title"]');
const findErrorMessage = () => wrapper.find('.js-error-state');
const findTableRows = () => wrapper.findAll('tbody tr');
diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
index 94d4cccab5f..1aeb080aa04 100644
--- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
@@ -1,4 +1,4 @@
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MrCollapsibleSection from '~/vue_merge_request_widget/components/mr_collapsible_extension.vue';
@@ -15,12 +15,14 @@ describe('Merge Request Collapsible Extension', () => {
},
slots: {
default: '<div class="js-slot">Foo</div>',
+ header: '<span data-testid="collapsed-header">hello there</span>',
},
});
};
- const findTitle = () => wrapper.find('.js-title');
+ const findTitle = () => wrapper.find('[data-testid="mr-collapsible-title"]');
const findErrorMessage = () => wrapper.find('.js-error-state');
+ const findIcon = () => wrapper.find(GlIcon);
afterEach(() => {
wrapper.destroy();
@@ -35,8 +37,12 @@ describe('Merge Request Collapsible Extension', () => {
expect(findTitle().text()).toBe(data.title);
});
+ it('renders the header slot', () => {
+ expect(wrapper.find('[data-testid="collapsed-header"]').text()).toBe('hello there');
+ });
+
it('renders angle-right icon', () => {
- expect(wrapper.vm.arrowIconName).toBe('angle-right');
+ expect(findIcon().props('name')).toBe('angle-right');
});
describe('onClick', () => {
@@ -54,7 +60,7 @@ describe('Merge Request Collapsible Extension', () => {
});
it('renders angle-down icon', () => {
- expect(wrapper.vm.arrowIconName).toBe('angle-down');
+ expect(findIcon().props('name')).toBe('angle-down');
});
});
});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
index 3baade5161e..f252257651e 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
@@ -1,7 +1,9 @@
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
+import { trimText } from 'helpers/text_helper';
import axios from '~/lib/utils/axios_utils';
import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue';
+import Deployment from '~/vue_merge_request_widget/components/deployment/deployment.vue';
import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue';
import { mockStore } from '../mock_data';
@@ -111,4 +113,50 @@ describe('MrWidgetPipelineContainer', () => {
expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true);
});
});
+ describe('with many deployments', () => {
+ let deployments;
+ let collapsibleExtension;
+
+ beforeEach(() => {
+ deployments = [
+ ...mockStore.deployments,
+ ...mockStore.deployments.map((deployment) => ({
+ ...deployment,
+ id: deployment.id + mockStore.deployments.length,
+ })),
+ ];
+ factory({
+ mr: {
+ ...mockStore,
+ deployments,
+ },
+ });
+ collapsibleExtension = wrapper.find('[data-testid="mr-collapsed-deployments"]');
+ });
+
+ it('renders them collapsed', () => {
+ expect(collapsibleExtension.exists()).toBe(true);
+ expect(trimText(collapsibleExtension.text())).toBe(
+ `${deployments.length} environments impacted. View all environments.`,
+ );
+ });
+
+ it('shows them when clicked', async () => {
+ const expectedProps = deployments.map((dep) =>
+ expect.objectContaining({
+ deployment: dep,
+ showMetrics: false,
+ }),
+ );
+ await collapsibleExtension.find('button').trigger('click');
+
+ const deploymentWrappers = collapsibleExtension.findAllComponents(Deployment);
+
+ expect(deploymentWrappers.wrappers.map((x) => x.props())).toEqual(expectedProps);
+ deploymentWrappers.wrappers.forEach((x) => {
+ expect(x.text()).toEqual(expect.any(String));
+ expect(x.text()).not.toBe('');
+ });
+ });
+ });
});
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index f23ffcee35d..0df04d2a8a7 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -332,4 +332,14 @@ RSpec.describe GitlabRoutingHelper do
end
end
end
+
+ context 'GraphQL ETag paths' do
+ context 'with pipelines' do
+ let(:pipeline) { double(id: 5) }
+
+ it 'returns an ETag path for pipelines' do
+ expect(graphql_etag_pipeline_path(pipeline)).to eq('/api/graphql:pipelines/id/5')
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
index 2923e589ff7..e5088653fab 100644
--- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
@@ -53,13 +53,15 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
context 'query generation' do
let(:expectation) do
<<~DOC
- ### Foo
+ ### foo
List of objects.
- | Name | Description | Type |
- | ----- | ---- | ----------- |
- | `id` | ID of the object. | ID |
+ #### Arguments
+
+ | Name | Type | Description |
+ | ---- | ---- | ----------- |
+ | `id` | ID | ID of the object. |
DOC
end
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index b4894ec049f..41e63e80649 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect { described_class.track_event(different_aggregation, values: entity1, time: Date.current) }.to raise_error(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownAggregation)
end
- it 'raise error if metrics of unknown aggregation' do
+ it 'raise error if metrics of unknown event' do
expect { described_class.track_event('unknown', values: entity1, time: Date.current) }.to raise_error(Gitlab::UsageDataCounters::HLLRedisCounter::UnknownEvent)
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 6517f2e9d68..9187dd4f300 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -790,6 +790,46 @@ module Ci
end
end
+ context 'when max queue depth is reached' do
+ let!(:pending_job) { create(:ci_build, :pending, :degenerated, pipeline: pipeline) }
+ let!(:pending_job_2) { create(:ci_build, :pending, :degenerated, pipeline: pipeline) }
+ let!(:pending_job_3) { create(:ci_build, :pending, pipeline: pipeline) }
+
+ before do
+ stub_const("#{described_class}::MAX_QUEUE_DEPTH", 2)
+ end
+
+ context 'when feature is enabled' do
+ before do
+ stub_feature_flags(gitlab_ci_builds_queue_limit: true)
+ end
+
+ it 'returns 409 conflict' do
+ expect(Ci::Build.pending.unstarted.count).to eq 3
+
+ result = described_class.new(specific_runner).execute
+
+ expect(result).not_to be_valid
+ expect(result.build).to be_nil
+ end
+ end
+
+ context 'when feature is disabled' do
+ before do
+ stub_feature_flags(gitlab_ci_builds_queue_limit: false)
+ end
+
+ it 'returns a valid result' do
+ expect(Ci::Build.pending.unstarted.count).to eq 3
+
+ result = described_class.new(specific_runner).execute
+
+ expect(result).to be_valid
+ expect(result.build).to eq pending_job_3
+ end
+ end
+ end
+
def execute(runner, params = {})
described_class.new(runner).execute(params).build
end
diff --git a/spec/services/system_notes/merge_requests_service_spec.rb b/spec/services/system_notes/merge_requests_service_spec.rb
index 2131f3d3bdf..58d2489f878 100644
--- a/spec/services/system_notes/merge_requests_service_spec.rb
+++ b/spec/services/system_notes/merge_requests_service_spec.rb
@@ -189,7 +189,7 @@ RSpec.describe ::SystemNotes::MergeRequestsService do
subject { service.change_branch('target', 'delete', old_branch, new_branch) }
it 'sets the note text' do
- expect(subject.note).to eq "changed automatically target branch to `#{new_branch}` because `#{old_branch}` was deleted"
+ expect(subject.note).to eq "deleted the `#{old_branch}` branch. This merge request now targets the `#{new_branch}` branch"
end
end