Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-16 21:09:08 +00:00
parent b58ab6c33c
commit fd11748fe8
29 changed files with 308 additions and 302 deletions

View File

@ -1,6 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon, GlButton } from '@gitlab/ui';
import { GlLoadingIcon, GlButton, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue';
@ -12,6 +12,9 @@ export default {
GlLoadingIcon,
GlButton,
},
directives: {
GlModal: GlModalDirective,
},
props: {
badge: {
type: Object,
@ -61,13 +64,13 @@ export default {
@click="editBadge(badge)"
/>
<gl-button
v-gl-modal.delete-badge-modal
:disabled="badge.isDeleting"
variant="danger"
data-toggle="modal"
data-target="#delete-badge-modal"
icon="remove"
size="medium"
:aria-label="__('Delete')"
data-testid="delete-badge"
@click="updateBadgeInModal(badge)"
/>
<gl-loading-icon v-show="badge.isDeleting" :inline="true" />

View File

@ -1,9 +1,8 @@
<script>
import { mapState, mapActions } from 'vuex';
import { GlSprintf } from '@gitlab/ui';
import { GlSprintf, GlModal } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
import Badge from './badge.vue';
import BadgeForm from './badge_form.vue';
import BadgeList from './badge_list.vue';
@ -14,7 +13,7 @@ export default {
Badge,
BadgeForm,
BadgeList,
GlModal: DeprecatedModal2,
GlModal,
GlSprintf,
},
i18n: {
@ -24,6 +23,17 @@ export default {
},
computed: {
...mapState(['badgeInModal', 'isEditing']),
primaryProps() {
return {
text: s__('Delete badge'),
attributes: [{ category: 'primary' }, { variant: 'danger' }],
};
},
cancelProps() {
return {
text: s__('Cancel'),
};
},
},
methods: {
...mapActions(['deleteBadge']),
@ -44,11 +54,11 @@ export default {
<template>
<div class="badge-settings">
<gl-modal
id="delete-badge-modal"
:header-title-text="s__('Badges|Delete badge?')"
:footer-primary-button-text="s__('Badges|Delete badge')"
footer-primary-button-variant="danger"
@submit="onSubmitModal"
modal-id="delete-badge-modal"
:title="s__('Badges|Delete badge?')"
:action-primary="primaryProps"
:action-cancel="cancelProps"
@primary="onSubmitModal"
>
<div class="well">
<badge
@ -65,9 +75,9 @@ export default {
</p>
</gl-modal>
<badge-form v-show="isEditing" :is-editing="true" />
<badge-form v-show="isEditing" :is-editing="true" data-testid="edit-badge" />
<badge-form v-show="!isEditing" :is-editing="false" />
<badge-form v-show="!isEditing" :is-editing="false" data-testid="add-new-badge" />
<badge-list v-show="!isEditing" />
</div>
</template>

View File

@ -1,5 +1,6 @@
<script>
import { GlButton, GlFormCheckbox, GlIcon, GlLink, GlAlert } from '@gitlab/ui';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
import CiLintResults from './ci_lint_results.vue';
import lintCIMutation from '../graphql/mutations/lint_ci.mutation.graphql';
@ -11,6 +12,7 @@ export default {
GlLink,
GlAlert,
CiLintResults,
EditorLite,
},
props: {
endpoint: {
@ -62,6 +64,9 @@ export default {
this.isErrorDismissed = false;
}
},
clear() {
this.content = '';
},
},
};
</script>
@ -76,22 +81,31 @@ export default {
@dismiss="isErrorDismissed = true"
>{{ apiError }}</gl-alert
>
<textarea v-model="content" cols="175" rows="20"></textarea>
<div class="file-holder gl-mb-3">
<div class="js-file-title file-title clearfix">
{{ __('Contents of .gitlab-ci.yml') }}
</div>
<editor-lite v-model="content" file-name="*.yml" />
</div>
</div>
<div class="col-sm-12 gl-display-flex gl-justify-content-space-between">
<div class="gl-display-flex gl-align-items-center">
<gl-button class="gl-mr-4" category="primary" variant="success" @click="lint">{{
__('Validate')
}}</gl-button>
<gl-button
class="gl-mr-4"
category="primary"
variant="success"
data-testid="ci-lint-validate"
@click="lint"
>{{ __('Validate') }}</gl-button
>
<gl-form-checkbox v-model="dryRun"
>{{ __('Simulate a pipeline created for the default branch') }}
<gl-link :href="helpPagePath" target="_blank"
><gl-icon class="gl-text-blue-600" name="question-o"/></gl-link
></gl-form-checkbox>
</div>
<gl-button>{{ __('Clear') }}</gl-button>
<gl-button data-testid="ci-lint-clear" @click="clear">{{ __('Clear') }}</gl-button>
</div>
<ci-lint-results

View File

@ -2,6 +2,7 @@ import $ from 'jquery';
import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from './flash';
import { fixTitle, hide } from '~/tooltips';
const tooltipTitles = {
group: __('Unsubscribe at group level'),
@ -59,9 +60,9 @@ export default class GroupLabelSubscription {
const type = $button.hasClass('js-group-level') ? 'group' : 'project';
const newTitle = tooltipTitles[type];
$('.js-unsubscribe-button', $button.closest('.label-actions-list'))
.tooltip('hide')
.attr('title', newTitle)
.tooltip('_fixTitle');
const $el = $('.js-unsubscribe-button', $button.closest('.label-actions-list'));
hide($el);
$el.attr('title', `${newTitle}`);
fixTitle($el);
}
}

View File

@ -1,13 +1,12 @@
<script>
import { GlIcon } from '@gitlab/ui';
import tooltip from '~/vue_shared/directives/tooltip';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
export default {
components: {
GlIcon,
},
directives: {
tooltip,
GlTooltip: GlTooltipDirective,
},
props: {
title: {
@ -51,7 +50,7 @@ export default {
<template>
<span
v-tooltip
v-gl-tooltip
:data-placement="tooltipPlacement"
:class="cssClass"
:title="title"

View File

@ -11,6 +11,8 @@ class Environment < ApplicationRecord
self.reactive_cache_hard_limit = 10.megabytes
self.reactive_cache_work_type = :external_dependency
PRODUCTION_ENVIRONMENT_IDENTIFIERS = %w[prod production].freeze
belongs_to :project, required: true
use_fast_destroy :all_deployments
@ -213,7 +215,7 @@ class Environment < ApplicationRecord
end
def update_merge_request_metrics?
folder_name == "production"
PRODUCTION_ENVIRONMENT_IDENTIFIERS.include?(folder_name.downcase)
end
def ref_path

View File

@ -1695,7 +1695,7 @@ class MergeRequest < ApplicationRecord
end
def allows_reviewers?
Feature.enabled?(:merge_request_reviewers, project)
Feature.enabled?(:merge_request_reviewers, project, default_enabled: true)
end
def allows_multiple_reviewers?

View File

@ -14,8 +14,8 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Config::Process,
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,
Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::Seed,
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
Gitlab::Ci::Pipeline::Chain::Seed,
Gitlab::Ci::Pipeline::Chain::Limit::Size,
Gitlab::Ci::Pipeline::Chain::Validate::External,
Gitlab::Ci::Pipeline::Chain::Populate,

View File

@ -55,7 +55,7 @@
- if merge_request.assignees.any?
%li.gl-display-flex.gl-align-items-center
= render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request
- if Feature.enabled?(:merge_request_reviewers, @project) && merge_request.reviewers.any?
- if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && merge_request.reviewers.any?
%li.gl-display-flex.issuable-reviewers
= render 'shared/issuable/reviewers', project: merge_request.project, issuable: merge_request
= render 'projects/merge_requests/approvals_count', merge_request: merge_request

View File

@ -25,7 +25,7 @@
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- if Feature.enabled?(:merge_request_reviewers, @project) && reviewers
- if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && reviewers
.block.reviewer.qa-reviewer-block
= render "shared/issuable/sidebar_reviewers", issuable_sidebar: issuable_sidebar, reviewers: reviewers, signed_in: signed_in

View File

@ -1,5 +0,0 @@
---
title: Fix workflow:rules not accessing passed-upstream and trigger variables
merge_request: 44935
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Update delete badge modal to gl-modal
merge_request: 44495
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Allow more naming conventions for VSA production environment
merge_request: 45069
author:
type: changed

View File

@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40488
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245190
group: group::source code
type: development
default_enabled: false
default_enabled: true

View File

@ -14557,9 +14557,9 @@ CREATE TABLE plan_limits (
nuget_max_file_size bigint DEFAULT 524288000 NOT NULL,
pypi_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL,
generic_packages_max_file_size bigint DEFAULT '5368709120'::bigint NOT NULL,
project_feature_flags integer DEFAULT 200 NOT NULL,
golang_max_file_size bigint DEFAULT 104857600 NOT NULL,
debian_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL,
project_feature_flags integer DEFAULT 200 NOT NULL,
ci_max_artifact_size_api_fuzzing integer DEFAULT 0 NOT NULL
);

View File

@ -120,6 +120,10 @@ Note the following when promoting a secondary:
1. Edit `/etc/gitlab/gitlab.rb` to reflect its new status as **primary** by
removing any lines that enabled the `geo_secondary_role`:
Users of GitLab 13.5 or later can skip this step, due to the appropriate
roles being enabled or disabled during the promotion in the following
step.
```ruby
## In pre-11.5 documentation, the role was enabled as follows. Remove this line.
geo_secondary_role['enable'] = true

View File

@ -553,7 +553,7 @@ POST /runners
|--------------|---------|----------|---------------------|
| `token` | string | yes | [Registration token](#registration-and-authentication-tokens). |
| `description`| string | no | Runner's description|
| `info` | hash | no | Runner's metadata |
| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version` is displayed in the Admin area of the UI. |
| `active` | boolean | no | Whether the runner is active |
| `locked` | boolean | no | Whether the runner should be locked for current project |
| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |

View File

@ -96,8 +96,7 @@ Value Stream Analytics records stage time and data based on the project issues w
exception of the staging stage, where only data deployed to
production are measured.
Specifically, if your CI is not set up and you have not defined a `production`
or `production/*` [environment](../../ci/yaml/README.md#environment), then you will not have any
Specifically, if your CI is not set up and you have not defined a [production environment](#how-the-production-environment-is-identified), then you will not have any
data for this stage.
Each stage of Value Stream Analytics is further described in the table below.
@ -109,7 +108,7 @@ Each stage of Value Stream Analytics is further described in the table below.
| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the issue closing pattern is not present in the merge request description, the MR is not considered to the measurement time of the stage. |
| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI/CD takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
| Review | Measures the median time taken to review the merge request that has a closing issue pattern, between its creation and until it's merged. |
| Staging | Measures the median time between merging the merge request with a closing issue pattern until the very first deployment to production. It's tracked by the environment set to `production` or matching `production/*` (case-sensitive, `Production` won't work) in your GitLab CI/CD configuration. If there isn't a production environment, this is not tracked. |
| Staging | Measures the median time between merging the merge request with a closing issue pattern until the very first deployment to a [production environment](#how-the-production-environment-is-identified). If there isn't a production environment, this is not tracked. |
How this works, behind the scenes:
@ -128,8 +127,18 @@ Value Stream Analytics dashboard will not present any data for:
- Merge requests that do not close an issue.
- Issues not labeled with a label present in the Issue Board or for issues not assigned a milestone.
- Staging stage, if the project has no `production` or `production/*`
environment.
- Staging stage, if the project has no [production environment](#how-the-production-environment-is-identified).
## How the production environment is identified
Value Stream Analytics identifies production environments by looking for project [environments](../../ci/yaml/README.md#environment) with a name matching any of these patterns:
- `prod` or `prod/*`
- `production` or `production/*`
These patterns are not case-sensitive.
You can change the name of a project environment in your GitLab CI/CD configuration.
## Example workflow

View File

@ -111,6 +111,48 @@ It is also possible to manage multiple assignees:
- When creating a merge request.
- Using [quick actions](../quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
## Reviewer
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216054) in GitLab 13.5.
> - It's [deployed behind a feature flag](../../../user/feature_flags.md), enabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-merge-request-reviewers). **(CORE ONLY)**
CAUTION: **Warning:**
This feature might not be available to you. Check the **version history** note above for details.
Requesting a code review is an important part of contributing code. However, deciding who should review
your code and asking for a review are no easy tasks. Using the "assignee" field for both authors and
reviewers makes it hard for others to determine who's doing what on a merge request.
GitLab's Merge Request Reviewers easily allow authors to request a review as well as see the status of the
review. By selecting one or more users from the **Reviewers** field in the merge request's right-hand
sidebar, the assigned reviewers will receive a notification of the request to review the merge request.
This makes it easy to determine the relevant roles for the users involved in the merge request, as well as formally requesting a review from a peer.
To request it, open the **Reviewers** drop-down box to search for the user you wish to get a review from.
### Enable or disable Merge Request Reviewers **(CORE ONLY)**
Merge Request Reviewers is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:merge_request_reviewers)
```
To disable it:
```ruby
Feature.disable(:merge_request_reviewers)
```
### Merge requests to close issues
If the merge request is being created to resolve an issue, you can

View File

@ -9,7 +9,7 @@ variables:
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, sobelow, pmd-apex, kubesec"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, sobelow, pmd-apex, kubesec, mobsf"
SAST_EXCLUDED_PATHS: "spec, test, tests, tmp"
SAST_ANALYZER_IMAGE_TAG: 2
SCAN_KUBERNETES_MANIFESTS: "false"
@ -125,6 +125,42 @@ gosec-sast:
exists:
- '**/*.go'
mobsf-android-sast:
extends: .sast-analyzer
services:
- name: opensecurity/mobile-security-framework-mobsf:latest
alias: mobsf
image:
name: "$SAST_ANALYZER_IMAGE"
variables:
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG"
rules:
- if: $SAST_DISABLED
when: never
- if: $CI_COMMIT_BRANCH &&
$SAST_DEFAULT_ANALYZERS =~ /mobsf/ &&
$SAST_EXPERIMENTAL_FEATURES == 'true'
exists:
- '**/AndroidManifest.xml'
mobsf-ios-sast:
extends: .sast-analyzer
services:
- name: opensecurity/mobile-security-framework-mobsf:latest
alias: mobsf
image:
name: "$SAST_ANALYZER_IMAGE"
variables:
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG"
rules:
- if: $SAST_DISABLED
when: never
- if: $CI_COMMIT_BRANCH &&
$SAST_DEFAULT_ANALYZERS =~ /mobsf/ &&
$SAST_EXPERIMENTAL_FEATURES == 'true'
exists:
- '**/*.xcodeproj/*'
nodejs-scan-sast:
extends: .sast-analyzer
image:
@ -203,6 +239,11 @@ spotbugs-sast:
variables:
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/spotbugs:$SAST_ANALYZER_IMAGE_TAG"
rules:
- if: $SAST_DEFAULT_ANALYZERS =~ /mobsf/ &&
$SAST_EXPERIMENTAL_FEATURES == 'true'
exists:
- '**/AndroidManifest.xml'
when: never
- if: $SAST_DISABLED
when: never
- if: $CI_COMMIT_BRANCH &&

View File

@ -4028,9 +4028,6 @@ msgstr ""
msgid "Badges|Badge image preview"
msgstr ""
msgid "Badges|Delete badge"
msgstr ""
msgid "Badges|Delete badge?"
msgstr ""
@ -8561,6 +8558,9 @@ msgstr ""
msgid "Delete artifacts"
msgstr ""
msgid "Delete badge"
msgstr ""
msgid "Delete board"
msgstr ""

View File

@ -1,117 +1,71 @@
import Vue from 'vue';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import store from '~/badges/store';
import BadgeSettings from '~/badges/components/badge_settings.vue';
import BadgeList from '~/badges/components/badge_list.vue';
import BadgeListRow from '~/badges/components/badge_list_row.vue';
import { createDummyBadge } from '../dummy_badge';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('BadgeSettings component', () => {
const Component = Vue.extend(BadgeSettings);
let vm;
let wrapper;
const badge = createDummyBadge();
const createComponent = (isEditing = false) => {
store.state.badges = [badge];
store.state.kind = 'project';
store.state.isEditing = isEditing;
wrapper = shallowMount(BadgeSettings, {
store,
localVue,
stubs: {
'badge-list': BadgeList,
'badge-list-row': BadgeListRow,
},
});
};
beforeEach(() => {
setFixtures(`
<div id="dummy-element"></div>
<button
id="dummy-modal-button"
type="button"
data-toggle="modal"
data-target="#delete-badge-modal"
>Show modal</button>
`);
// Can be removed once GlLoadingIcon no longer throws a warning
jest.spyOn(global.console, 'warn').mockImplementation(() => jest.fn());
vm = mountComponentWithStore(Component, {
el: '#dummy-element',
store,
});
createComponent();
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
it('displays modal if button is clicked', done => {
const badge = createDummyBadge();
store.state.badgeInModal = badge;
const modal = vm.$el.querySelector('#delete-badge-modal');
const button = document.getElementById('dummy-modal-button');
it('displays modal if button for deleting a badge is clicked', async () => {
const button = wrapper.find('[data-testid="delete-badge"]');
button.click();
button.vm.$emit('click');
await wrapper.vm.$nextTick();
Vue.nextTick()
.then(() => {
expect(modal.innerText).toMatch('Delete badge?');
const badgeElement = modal.querySelector('img.project-badge');
expect(badgeElement).not.toBe(null);
expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl);
})
.then(done)
.catch(done.fail);
const modal = wrapper.find(GlModal);
expect(modal.isVisible()).toBe(true);
});
it('displays a form to add a badge', () => {
const form = vm.$el.querySelector('form:nth-of-type(2)');
expect(form).not.toBe(null);
const button = form.querySelector('.btn-success');
expect(button).not.toBe(null);
expect(button).toHaveText(/Add badge/);
expect(wrapper.find('[data-testid="add-new-badge"]').isVisible()).toBe(true);
});
it('displays badge list', () => {
const badgeListElement = vm.$el.querySelector('.card');
expect(badgeListElement).not.toBe(null);
expect(badgeListElement).toBeVisible();
expect(badgeListElement.innerText).toMatch('Your badges');
expect(wrapper.find(BadgeList).isVisible()).toBe(true);
});
describe('when editing', () => {
beforeEach(done => {
store.state.isEditing = true;
Vue.nextTick()
.then(done)
.catch(done.fail);
beforeEach(() => {
createComponent(true);
});
it('displays a form to edit a badge', () => {
const form = vm.$el.querySelector('form:nth-of-type(1)');
expect(form).not.toBe(null);
const cancelButton = form.querySelector('[data-testid="cancelEditing"]');
expect(cancelButton).not.toBe(null);
expect(cancelButton).toHaveText(/Cancel/);
const submitButton = form.querySelector('[data-testid="saveEditing"]');
expect(submitButton).not.toBe(null);
expect(submitButton).toHaveText(/Save changes/);
expect(wrapper.find('[data-testid="edit-badge"]').isVisible()).toBe(true);
});
it('displays no badge list', () => {
const badgeListElement = vm.$el.querySelector('.card');
expect(badgeListElement).toBeHidden();
});
});
describe('methods', () => {
describe('onSubmitModal', () => {
it('triggers ', () => {
jest.spyOn(vm, 'deleteBadge').mockImplementation(() => Promise.resolve());
const modal = vm.$el.querySelector('#delete-badge-modal');
const deleteButton = modal.querySelector('.btn-danger');
deleteButton.click();
const badge = store.state.badgeInModal;
expect(vm.deleteBadge).toHaveBeenCalledWith(badge);
});
expect(wrapper.find(BadgeList).isVisible()).toBe(false);
});
});
});

View File

@ -0,0 +1,77 @@
import { shallowMount } from '@vue/test-utils';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
import CiLint from '~/ci_lint/components/ci_lint.vue';
import lintCIMutation from '~/ci_lint/graphql/mutations/lint_ci.mutation.graphql';
describe('CI Lint', () => {
let wrapper;
const endpoint = '/namespace/project/-/ci/lint';
const content =
"test_job:\n stage: build\n script: echo 'Building'\n only:\n - web\n - chat\n - pushes\n allow_failure: true ";
const createComponent = () => {
wrapper = shallowMount(CiLint, {
data() {
return {
content,
};
},
propsData: {
endpoint,
helpPagePath: '/help/ci/lint#pipeline-simulation',
},
mocks: {
$apollo: {
mutate: jest.fn(),
},
},
});
};
const findEditor = () => wrapper.find(EditorLite);
const findValidateBtn = () => wrapper.find('[data-testid="ci-lint-validate"]');
const findClearBtn = () => wrapper.find('[data-testid="ci-lint-clear"]');
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('displays the editor', () => {
expect(findEditor().exists()).toBe(true);
});
it('validate action calls mutation correctly', () => {
findValidateBtn().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: lintCIMutation,
variables: { content, dry: false, endpoint },
});
});
it('validate action calls mutation with dry run', async () => {
const dryRunEnabled = true;
await wrapper.setData({ dryRun: dryRunEnabled });
findValidateBtn().vm.$emit('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: lintCIMutation,
variables: { content, dry: dryRunEnabled, endpoint },
});
});
it('content is cleared on clear action', async () => {
expect(findEditor().props('value')).toBe(content);
await findClearBtn().vm.$emit('click');
expect(findEditor().props('value')).toBe('');
});
});

View File

@ -203,7 +203,7 @@ describe('GroupItemComponent', () => {
expect(vm.$el.querySelector('.title a.no-expand')).toBeDefined();
expect(visibilityIconEl).not.toBe(null);
expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
expect(visibilityIconEl.title).toBe(vm.visibilityTooltip);
expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0);
expect(vm.$el.querySelector('.access-type')).toBeDefined();

View File

@ -49,7 +49,7 @@ describe('ItemStatsValue', () => {
});
it('renders element tooltip correctly', () => {
expect(wrapper.attributes('data-original-title')).toBe('Subgroups');
expect(wrapper.attributes('title')).toBe('Subgroups');
expect(wrapper.attributes('data-placement')).toBe('left');
});

View File

@ -312,18 +312,25 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
describe '#update_merge_request_metrics?' do
{
'gprd' => false,
'prod' => true,
'prod-test' => false,
'PROD' => true,
'production' => true,
'production-test' => false,
'PRODUCTION' => true,
'production/eu' => true,
'PRODUCTION/EU' => true,
'production/www.gitlab.com' => true,
'productioneu' => false,
'Production' => false,
'Production/eu' => false,
'Production' => true,
'Production/eu' => true,
'test-production' => false
}.each do |name, expected_value|
it "returns #{expected_value} for #{name}" do
env = create(:environment, name: name)
expect(env.update_merge_request_metrics?).to eq(expected_value)
expect(env.update_merge_request_metrics?).to eq(expected_value), "Expected the name '#{name}' to result in #{expected_value}, but it didn't."
end
end
end

View File

@ -581,40 +581,5 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
)
end
end
context 'when downstream pipeline has workflow rule' do
before do
stub_ci_pipeline_yaml_file(config)
end
let(:config) do
<<-EOY
workflow:
rules:
- if: $my_var
regular-job:
script: 'echo Hello, World!'
EOY
end
context 'when passing the required variable' do
before do
bridge.yaml_variables = [{ key: 'my_var', value: 'var', public: true }]
end
it 'creates the pipeline' do
expect { service.execute(bridge) }.to change(downstream_project.ci_pipelines, :count).by(1)
expect(bridge.reload).to be_success
end
end
context 'when not passing the required variable' do
it 'does not create the pipeline' do
expect { service.execute(bridge) }.not_to change(downstream_project.ci_pipelines, :count)
end
end
end
end
end

View File

@ -41,9 +41,7 @@ RSpec.describe Ci::CreatePipelineService do
save_on_errors: save_on_errors,
trigger_request: trigger_request,
merge_request: merge_request,
external_pull_request: external_pull_request) do |pipeline|
yield(pipeline) if block_given?
end
external_pull_request: external_pull_request)
end
# rubocop:enable Metrics/ParameterLists
@ -2276,108 +2274,6 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
context 'with workflow rules with persisted variables' do
let(:config) do
<<-EOY
workflow:
rules:
- if: $CI_COMMIT_REF_NAME == "master"
regular-job:
script: 'echo Hello, World!'
EOY
end
context 'with matches' do
it 'creates a pipeline' do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('regular-job')
end
end
context 'with no matches' do
let(:ref_name) { 'refs/heads/feature' }
it 'does not create a pipeline' do
expect(pipeline).not_to be_persisted
end
end
end
context 'with workflow rules with pipeline variables' do
let(:pipeline) do
execute_service(variables_attributes: variables_attributes)
end
let(:config) do
<<-EOY
workflow:
rules:
- if: $SOME_VARIABLE
regular-job:
script: 'echo Hello, World!'
EOY
end
context 'with matches' do
let(:variables_attributes) do
[{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
end
it 'creates a pipeline' do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('regular-job')
end
end
context 'with no matches' do
let(:variables_attributes) { {} }
it 'does not create a pipeline' do
expect(pipeline).not_to be_persisted
end
end
end
context 'with workflow rules with trigger variables' do
let(:pipeline) do
execute_service do |pipeline|
pipeline.variables.build(variables)
end
end
let(:config) do
<<-EOY
workflow:
rules:
- if: $SOME_VARIABLE
regular-job:
script: 'echo Hello, World!'
EOY
end
context 'with matches' do
let(:variables) do
[{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
end
it 'creates a pipeline' do
expect(pipeline).to be_persisted
expect(build_names).to contain_exactly('regular-job')
end
end
context 'with no matches' do
let(:variables) { {} }
it 'does not create a pipeline' do
expect(pipeline).not_to be_persisted
end
end
end
end
end

View File

@ -161,29 +161,6 @@ RSpec.describe Ci::PipelineTriggerService do
expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables)
expect(job.sourced_pipelines.last.pipeline_id).to eq(result[:pipeline].id)
end
context 'when the config has workflow rule with the variable' do
let(:config) do
<<-EOY
workflow:
rules:
- if: $AAA
regular-job:
script: 'echo Hello, World!'
EOY
end
before do
stub_ci_pipeline_yaml_file(config)
end
it 'runs the pipeline' do
expect { result }.to change { Ci::Pipeline.count }.by(1)
expect(result[:status]).to eq(:success)
end
end
end
end