Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-01 15:22:06 +00:00
parent fbc7e1f503
commit 2ccb5c91c8
86 changed files with 466 additions and 443 deletions

View file

@ -121,6 +121,11 @@ yarn-audit-dependency_scanning:
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
script:
- DEBUG=* node /usr/src/app/cli.js analyze --format gitlab --manager ${PACKAGE_MANAGER} gitlab.tgz | tee ${CI_PROJECT_DIR}/gl-dependency-scanning-report.json
after_script:
- mkdir ~/.aws
- '[[ -z "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ]] || mv "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ~/.aws/credentials'
- npm install --no-save --ignore-scripts @aws-sdk/client-s3@3.49.0
- scripts/ingest-reports-to-siem
artifacts:
paths:
- gl-dependency-scanning-report.json

View file

@ -17,9 +17,6 @@ export default {
revokePath: {
default: '',
},
buttonClass: {
default: '',
},
},
computed: {
modalId() {
@ -38,10 +35,9 @@ export default {
<div>
<gl-button
v-gl-modal="modalId"
:class="buttonClass"
category="primary"
variant="danger"
class="float-right"
class="gl-float-right"
data-testid="revoke-button"
>{{ s__('DeployTokens|Revoke') }}</gl-button
>

View file

@ -9,14 +9,13 @@ export default () => {
}
return containers.forEach((el) => {
const { token, revokePath, buttonClass } = el.dataset;
const { token, revokePath } = el.dataset;
return new Vue({
el,
provide: {
token: JSON.parse(token),
revokePath,
buttonClass,
},
render(h) {
return h(RevokeButton);

View file

@ -83,17 +83,17 @@ export default {
i18n: {
sectionTitle: s__('JiraService|View Jira issues in GitLab'),
sectionDescription: s__(
'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only.',
'JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues.',
),
enableCheckboxLabel: s__('JiraService|Enable Jira issues'),
enableCheckboxHelp: s__(
'JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below.',
'JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select.',
),
projectKeyLabel: s__('JiraService|Jira project key'),
projectKeyPlaceholder: s__('JiraService|For example, AB'),
requiredFieldFeedback: __('This field is required.'),
issueTrackerConflictWarning: s__(
'JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used.',
'JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used.',
),
},
};

View file

@ -138,7 +138,7 @@ export default {
label-for="service[trigger]"
:description="
s__(
'Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled).',
'JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created.',
)
"
>

View file

@ -264,7 +264,7 @@ export default {
<gl-button
:disabled="submitDisabled"
:loading="isLoading"
variant="success"
variant="confirm"
data-qa-selector="invite_button"
data-testid="invite-button"
@click="submit"

View file

@ -135,6 +135,14 @@ export default {
const canReopen = this.isClosed && this.canReopenIssue;
return canClose || canReopen;
},
hasDesktopDropdown() {
return (
this.canCreateIssue || this.canPromoteToEpic || !this.isIssueAuthor || this.canReportSpam
);
},
hasMobileDropdown() {
return this.hasDesktopDropdown || this.showToggleIssueStateButton;
},
},
created() {
eventHub.$on('toggle.issuable.state', this.toggleIssueState);
@ -223,10 +231,12 @@ export default {
<template>
<div class="detail-page-header-actions gl-display-flex">
<gl-dropdown
v-if="hasMobileDropdown"
class="gl-sm-display-none! w-100"
block
:text="dropdownText"
data-qa-selector="issue_actions_dropdown"
data-testid="mobile-dropdown"
:loading="isToggleStateButtonLoading"
>
<gl-dropdown-item
@ -276,11 +286,13 @@ export default {
</gl-button>
<gl-dropdown
v-if="hasDesktopDropdown"
class="gl-display-none gl-sm-display-inline-flex! gl-ml-3"
icon="ellipsis_v"
category="tertiary"
:text="dropdownText"
:text-sr-only="true"
data-testid="desktop-dropdown"
no-caret
right
>

View file

@ -1,5 +1,5 @@
<script>
import { GlSprintf, GlButton, GlButtonGroup } from '@gitlab/ui';
import { GlSprintf, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mapGetters, mapState, mapActions } from 'vuex';
import { __ } from '~/locale';
import FileIcon from '~/vue_shared/components/file_icon.vue';
@ -23,6 +23,7 @@ export default {
GlButton,
GlButtonGroup,
GlSprintf,
GlLoadingIcon,
FileIcon,
DiffFileEditor,
InlineConflictLines,
@ -72,9 +73,7 @@ export default {
</script>
<template>
<div id="conflicts">
<div v-if="isLoading" class="loading">
<div class="spinner spinner-md"></div>
</div>
<gl-loading-icon v-if="isLoading" size="md" data-testid="loading-spinner" />
<div v-if="hasError" class="nothing-here-block">
{{ conflictsData.errorMessage }}
</div>

View file

@ -69,9 +69,10 @@ export default {
// If it's a brand new file, we don't want to fetch the content.
// Then when the user commits the first time, the query would run
// to get the initial file content, but we already have it in `lastCommitedContent`
// so we skip the loading altogether.
skip({ isNewCiConfigFile, lastCommittedContent }) {
return isNewCiConfigFile || lastCommittedContent;
// so we skip the loading altogether. We also wait for the currentBranch
// to have been fetched
skip() {
return this.shouldSkipBlobContentQuery;
},
variables() {
return {
@ -128,8 +129,8 @@ export default {
},
ciConfigData: {
query: getCiConfigData,
skip({ currentCiFileContent }) {
return !currentCiFileContent;
skip() {
return this.shouldSkipCiConfigQuery;
},
variables() {
return {
@ -174,6 +175,9 @@ export default {
},
commitSha: {
query: getLatestCommitShaQuery,
skip({ currentBranch }) {
return !currentBranch;
},
variables() {
return {
projectPath: this.projectFullPath,
@ -181,7 +185,7 @@ export default {
};
},
update(data) {
const latestCommitSha = data.project?.repository?.tree?.lastCommit?.sha;
const latestCommitSha = data?.project?.repository?.tree?.lastCommit?.sha;
if (this.isFetchingCommitSha && latestCommitSha === this.commitSha) {
this.$apollo.queries.commitSha.startPolling(COMMIT_SHA_POLL_INTERVAL);
@ -192,6 +196,9 @@ export default {
this.$apollo.queries.commitSha.stopPolling();
return latestCommitSha;
},
error() {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
},
},
currentBranch: {
query: getCurrentBranch,
@ -234,6 +241,12 @@ export default {
isEmpty() {
return this.currentCiFileContent === '';
},
shouldSkipBlobContentQuery() {
return this.isNewCiConfigFile || this.lastCommittedContent || !this.currentBranch;
},
shouldSkipCiConfigQuery() {
return !this.currentCiFileContent || !this.commitSha;
},
},
i18n: {
resetModal: {

View file

@ -5,7 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '../components/runner_header.vue';
import RunnerUpdateForm from '../components/runner_update_form.vue';
import { I18N_FETCH_ERROR } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql';
import runnerQuery from '../graphql/details/runner.query.graphql';
import { captureException } from '../sentry_utils';
export default {
@ -27,7 +27,7 @@ export default {
},
apollo: {
runner: {
query: getRunnerQuery,
query: runnerQuery,
variables() {
return {
id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),

View file

@ -8,7 +8,7 @@ import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
import RunnerDetails from '../components/runner_details.vue';
import { I18N_FETCH_ERROR } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql';
import runnerQuery from '../graphql/details/runner.query.graphql';
import { captureException } from '../sentry_utils';
export default {
@ -35,7 +35,7 @@ export default {
},
apollo: {
runner: {
query: getRunnerQuery,
query: runnerQuery,
variables() {
return {
id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),

View file

@ -26,8 +26,8 @@ import {
STATUS_STALE,
I18N_FETCH_ERROR,
} from '../constants';
import getRunnersQuery from '../graphql/get_runners.query.graphql';
import getRunnersCountQuery from '../graphql/get_runners_count.query.graphql';
import runnersAdminQuery from '../graphql/list/admin_runners.query.graphql';
import runnersAdminCountQuery from '../graphql/list/admin_runners_count.query.graphql';
import {
fromUrlQueryToSearch,
fromSearchToUrl,
@ -36,7 +36,7 @@ import {
import { captureException } from '../sentry_utils';
const runnersCountSmartQuery = {
query: getRunnersCountQuery,
query: runnersAdminCountQuery,
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
update(data) {
return data?.runners?.count;
@ -77,7 +77,7 @@ export default {
},
apollo: {
runners: {
query: getRunnersQuery,
query: runnersAdminQuery,
// Runners can be updated by users directly in this list.
// A "cache and network" policy prevents outdated filtered
// results.

View file

@ -4,7 +4,7 @@ import { createAlert } from '~/flash';
import { TYPE_GROUP, TYPE_PROJECT } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __, s__ } from '~/locale';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql';
import { captureException } from '~/runner/sentry_utils';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../../constants';

View file

@ -1,6 +1,6 @@
<script>
import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql';
import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql';
import { createAlert } from '~/flash';
import { s__, sprintf } from '~/locale';
import { captureException } from '~/runner/sentry_utils';

View file

@ -1,7 +1,7 @@
<script>
import { GlSkeletonLoading } from '@gitlab/ui';
import { createAlert } from '~/flash';
import getRunnerJobsQuery from '../graphql/get_runner_jobs.query.graphql';
import runnerJobsQuery from '../graphql/details/runner_jobs.query.graphql';
import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants';
import { captureException } from '../sentry_utils';
import { getPaginationVariables } from '../utils';
@ -34,7 +34,7 @@ export default {
},
apollo: {
jobs: {
query: getRunnerJobsQuery,
query: runnerJobsQuery,
variables() {
return this.variables;
},

View file

@ -1,6 +1,6 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql';
import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql';
import { createAlert } from '~/flash';
import { captureException } from '~/runner/sentry_utils';
import { I18N_PAUSE, I18N_RESUME } from '../constants';

View file

@ -2,7 +2,7 @@
import { GlSkeletonLoading } from '@gitlab/ui';
import { sprintf, formatNumber } from '~/locale';
import { createAlert } from '~/flash';
import getRunnerProjectsQuery from '../graphql/get_runner_projects.query.graphql';
import runnerProjectsQuery from '../graphql/details/runner_projects.query.graphql';
import {
I18N_ASSIGNED_PROJECTS,
I18N_NONE,
@ -41,7 +41,7 @@ export default {
},
apollo: {
projects: {
query: getRunnerProjectsQuery,
query: runnerProjectsQuery,
variables() {
return this.variables;
},

View file

@ -15,7 +15,7 @@ import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { __ } from '~/locale';
import { captureException } from '~/runner/sentry_utils';
import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
import runnerUpdateMutation from '../graphql/runner_update.mutation.graphql';
import runnerUpdateMutation from '../graphql/details/runner_update.mutation.graphql';
export default {
name: 'RunnerUpdateForm',

View file

@ -1,10 +1,9 @@
#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql"
#import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql"
query getRunner($id: CiRunnerID!) {
# We have an id in deeply nested fragment
# eslint-disable-next-line @graphql-eslint/require-id-when-available
runner(id: $id) {
__typename
...RunnerDetails
}
}

View file

@ -1,4 +1,5 @@
fragment RunnerDetailsShared on CiRunner {
__typename
id
runnerType
active
@ -22,7 +23,7 @@ fragment RunnerDetailsShared on CiRunner {
groups {
# Only a single group can be loaded here, while projects
# are loaded separately using the query with pagination
# parameters `get_runner_projects.query.graphql`.
# parameters `runner_projects.query.graphql`.
nodes {
id
avatarUrl

View file

@ -1,4 +1,4 @@
#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql"
#import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql"
# Mutation for updates from the runner form, loads
# attributes shown in the runner details.

View file

@ -1,4 +1,4 @@
#import "~/runner/graphql/runner_node.fragment.graphql"
#import "~/runner/graphql/list/list_item.fragment.graphql"
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query getRunners(
@ -24,7 +24,7 @@ query getRunners(
sort: $sort
) {
nodes {
...RunnerNode
...ListItem
adminUrl
editAdminUrl
}

View file

@ -1,4 +1,4 @@
#import "~/runner/graphql/runner_node.fragment.graphql"
#import "~/runner/graphql/list/list_item.fragment.graphql"
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query getGroupRunners(
@ -29,8 +29,7 @@ query getGroupRunners(
webUrl
editUrl
node {
__typename
...RunnerNode
...ListItem
}
}
pageInfo {

View file

@ -1,4 +1,4 @@
fragment RunnerNode on CiRunner {
fragment ListItem on CiRunner {
__typename
id
description

View file

@ -24,8 +24,8 @@ import {
STATUS_OFFLINE,
STATUS_STALE,
} from '../constants';
import getGroupRunnersQuery from '../graphql/get_group_runners.query.graphql';
import getGroupRunnersCountQuery from '../graphql/get_group_runners_count.query.graphql';
import groupRunnersQuery from '../graphql/list/group_runners.query.graphql';
import groupRunnersCountQuery from '../graphql/list/group_runners_count.query.graphql';
import {
fromUrlQueryToSearch,
fromSearchToUrl,
@ -34,7 +34,7 @@ import {
import { captureException } from '../sentry_utils';
const runnersCountSmartQuery = {
query: getGroupRunnersCountQuery,
query: groupRunnersCountQuery,
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
update(data) {
return data?.group?.runners?.count;
@ -84,7 +84,7 @@ export default {
},
apollo: {
runners: {
query: getGroupRunnersQuery,
query: groupRunnersQuery,
// Runners can be updated by users directly in this list.
// A "cache and network" policy prevents outdated filtered
// results.

View file

@ -3,10 +3,6 @@
class Projects::ClusterAgentsController < Projects::ApplicationController
before_action :authorize_can_read_cluster_agent!
before_action do
push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml)
end
feature_category :kubernetes_management
def show

View file

@ -3,20 +3,6 @@
class ApplicationExperiment < Gitlab::Experiment
control { nil } # provide a default control for anonymous experiments
def publish(_result = nil)
super
publish_to_client
end
def publish_to_client
return unless should_track?
Gon.push({ experiment: { name => signature } }, true)
rescue NoMethodError
# means we're not in the request cycle, and can't add to Gon. Log a warning maybe?
end
def publish_to_database
ActiveSupport::Deprecation.warn('publish_to_database is deprecated and should not be used for reporting anymore')

View file

@ -16,4 +16,11 @@ module DeployTokensHelper
Gitlab.config.packages.enabled &&
can?(current_user, :read_package, group_or_project)
end
def deploy_token_revoke_button_data(token:, group_or_project:)
{
token: token.to_json(only: [:id, :name]),
revoke_path: revoke_deploy_token_path(group_or_project, token)
}
end
end

View file

@ -111,8 +111,8 @@ module Integrations
end
def help
jira_doc_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_url('integration/jira/index.html') }
s_("JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}.") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe }
jira_doc_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('integration/jira/index.html') }
s_("JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe }
end
def title

View file

@ -40,12 +40,7 @@ class Namespace
SQL
Namespace.transaction do
if Feature.enabled?(:for_no_key_update_lock, default_enabled: :yaml)
@root.lock!("FOR NO KEY UPDATE")
else
@root.lock!
end
@root.lock!("FOR NO KEY UPDATE")
Namespace.connection.exec_query(sql)
end
rescue ActiveRecord::Deadlocked

View file

@ -44,22 +44,15 @@ module Namespaces
included do
before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? }
after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? }
# sync traversal_ids on namespace create, which can happen quite early within a transaction, thus keeping the lock on root namespace record
# for a relatively long time, e.g. creating the project namespace when a project is being created.
after_create :sync_traversal_ids, if: -> { sync_traversal_ids? && !sync_traversal_ids_before_commit? }
# This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed.
# This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid
before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? && sync_traversal_ids_before_commit? }
before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? }
end
def sync_traversal_ids?
Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml)
end
def sync_traversal_ids_before_commit?
Feature.enabled?(:sync_traversal_ids_before_commit, root_ancestor, default_enabled: :yaml)
end
def use_traversal_ids?
return false unless Feature.enabled?(:use_traversal_ids, default_enabled: :yaml)

View file

@ -22,15 +22,15 @@
= f.label :eks_account_id, _('Account ID'), class: 'label-bold'
= f.text_field :eks_account_id, class: 'form-control gl-form-input'
.form-group
= f.label :eks_access_key_id, _('Access key ID'), class: 'label-bold'
= f.label :eks_access_key_id, _('AWS access key ID (Optional)'), class: 'label-bold'
= f.text_field :eks_access_key_id, class: 'form-control gl-form-input'
.form-text.text-muted
= _('AWS Access Key. Only required if not using role instance credentials')
= _('Only required if not using role instance credentials.')
.form-group
= f.label :eks_secret_access_key, _('Secret access key'), class: 'label-bold'
= f.label :eks_secret_access_key, _('AWS secret access key (Optional)'), class: 'label-bold'
= f.password_field :eks_secret_access_key, autocomplete: 'off', class: 'form-control gl-form-input'
.form-text.text-muted
= _('AWS Secret Access Key. Only required if not using role instance credentials')
= _('Only required if not using role instance credentials.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"

View file

@ -12,7 +12,7 @@
- link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
= s_('SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance\'s code views and merge requests.').html_safe % { link_start: link_start, link_end: link_end }
%span
= link_to s_('SourcegraphAdmin|More information'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer'
= link_to s_('SourcegraphAdmin|Learn more.'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
@ -29,10 +29,10 @@
= f.check_box :sourcegraph_public_only, class: 'form-check-input'
= f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label'
.form-text.text-muted
= s_('SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph.')
= s_('SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph.')
.form-group
= f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold'
= f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|e.g. https://sourcegraph.example.com')
= f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|https://sourcegraph.example.com')
.form-text.text-muted
= s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.')
= f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-confirm'

View file

@ -15,7 +15,7 @@
.gl-w-half.gl-xs-w-full
.gl-display-flex.gl-flex-wrap.gl-justify-content-end.gl-mb-3
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full', display_text: _('Invite a group') } }
.js-invite-members-trigger{ data: { variant: 'success',
.js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
trigger_source: 'group-members-page',
display_text: _('Invite members') } }

View file

@ -23,7 +23,7 @@
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } }
= render 'projects/invite_groups_modal', project: @project
- if can_admin_project_member?(@project)
.js-invite-members-trigger{ data: { variant: 'success',
.js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
trigger_source: 'project-members-page',
display_text: _('Invite members') } }

View file

@ -25,7 +25,7 @@
%span.token-never-expires-label= _('Never')
%td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td
.js-deploy-token-revoke-button{ data: { button_class: 'float-right', token: token.to_json, revoke_path: revoke_deploy_token_path(group_or_project, token) } }
.js-deploy-token-revoke-button{ data: deploy_token_revoke_button_data(token: token, group_or_project: group_or_project) }
- else
.settings-message.text-center

View file

@ -1,8 +0,0 @@
---
name: cluster_vulnerabilities
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73321
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917
milestone: '14.5'
type: development
group: group::container security
default_enabled: true

View file

@ -1,8 +0,0 @@
---
name: for_no_key_update_lock
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81239
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353619
milestone: '14.9'
type: development
group: group::workspaces
default_enabled: false

View file

@ -1,8 +0,0 @@
---
name: sync_traversal_ids_before_commit
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79964
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352499
group: group::workspace
type: development
default_enabled: false
milestone: '14.8'

View file

@ -317,7 +317,7 @@ of tracking an event in Ruby would be:
experiment(:pill_color, actor: current_user).track(:clicked)
```
When you run an experiment with any of the examples so far, an `:assigned` event
When you run an experiment with any of the examples so far, an `:assignment` event
is tracked automatically by default. All events that are tracked from an
experiment have a special
[experiment context](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_experiment/jsonschema/1-0-3)

View file

@ -50,7 +50,7 @@ To configure your project:
If you enable Jira issues with this setting, all users with access to this GitLab project
can view all issues from the specified Jira project.
1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issues creation from vulnerabilities**.
1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issue creation from vulnerabilities**.
1. Select the **Jira issue type**. If the dropdown is empty, select refresh (**{retry}**) and try again.
1. To verify the Jira connection is working, select **Test settings**.
1. Select **Save changes**.

View file

@ -80,7 +80,7 @@ The issue is then opened so you can take further action.
Prerequisites:
- [Enable Jira integration](../../../integration/jira/index.md).
The **Enable Jira issues creation from vulnerabilities** option must be selected as part of the configuration.
The **Enable Jira issue creation from vulnerabilities** option must be selected as part of the configuration.
- Each user must have a personal Jira user account with permission to create issues in the target project.
To create a Jira issue for a vulnerability:

View file

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Container vulnerability scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8 [with a flag](../../../administration/feature_flags.md) named `cluster_vulnerabilities`. Enabled by default.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md).
You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab.

View file

@ -67,15 +67,15 @@ Features not found in standard Markdown:
Features [extended from standard Markdown](#features-extended-from-standard-markdown):
| Standard Markdown | Extended Markdown in GitLab |
| ------------------------------------- | ------------------------- |
| [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) |
| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) |
| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis)
| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) |
| [images](#images) | [embedded videos](#videos) and [audio](#audio) |
| [line breaks](#line-breaks) | [more line break control](#newlines) |
| [links](#links) | [automatically linking URLs](#url-auto-linking) |
| Standard Markdown | Extended Markdown in GitLab |
|---------------------------------------|---------------------------------------------------------------------------------------|
| [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) |
| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) |
| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) |
| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) |
| [images](#images) | [embedded videos](#videos) and [audio](#audio) |
| [line breaks](#line-breaks) | [more line break control](#newlines) |
| [links](#links) | [automatically linking URLs](#url-auto-linking) |
## Features not found in standard Markdown
@ -262,7 +262,7 @@ The following delimiters are supported:
---
title: About Front Matter
example:
language: yaml
language: yaml
---
```
@ -515,31 +515,31 @@ version to reference other projects from the same namespace.
GitLab Flavored Markdown recognizes the following:
| references | input | cross-project reference | shortcut inside same namespace |
| :--------------------------------------------------- | :---------------------------- | :----------------------------------------- | :------------------------------- |
| specific user | `@user_name` | | |
| specific group | `@group_name` | | |
| entire team | `@all` | | |
| project | `namespace/project>` | | |
| issue | ``#123`` | `namespace/project#123` | `project#123` |
| merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` |
| [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | |
| vulnerability **(ULTIMATE)** <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
| feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` |
| label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` |
| project milestone by ID | `%123` | `namespace/project%123` | `project%123` |
| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
| repository file references | `[README](doc/README.md)` | | |
| repository file line references | `[README](doc/README.md#L13)` | | |
| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
| contact | `[contact:test@example.com]` | | |
| references | input | cross-project reference | shortcut inside same namespace |
|:----------------------------------------------------------------------------|:------------------------------|:----------------------------------------|:-------------------------------|
| specific user | `@user_name` | | |
| specific group | `@group_name` | | |
| entire team | `@all` | | |
| project | `namespace/project>` | | |
| issue | ``#123`` | `namespace/project#123` | `project#123` |
| merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` |
| [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | |
| [vulnerability](application_security/vulnerabilities/index.md) <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
| feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` |
| label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` |
| project milestone by ID | `%123` | `namespace/project%123` | `project%123` |
| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
| repository file references | `[README](doc/README.md)` | | |
| repository file line references | `[README](doc/README.md#L13)` | | |
| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
| contact | `[contact:test@example.com]` | | |
1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222483) in GitLab 13.7.
@ -1486,7 +1486,7 @@ but they do not render properly on `docs.gitlab.com`:
#### Copy from spreadsheet and paste in Markdown
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7.
If you're working in spreadsheet software (for example, Microsoft Excel, Google
Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy-and-paste

View file

@ -34,7 +34,8 @@ in the search field in the upper right corner:
> - Filtering by epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in GitLab 12.9.
> - Filtering by child epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in GitLab 13.0.
> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6. Moved from GitLab Ultimate to Premium in 13.9.
> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6.
> - Filtering by iterations was moved from GitLab Ultimate to GitLab Premium in 13.9.
> - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default.
Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and
@ -127,14 +128,14 @@ the dropdown list) **Approved-By** and select the user.
![Filter MRs by approved by](img/filter_approved_by_merge_requests_v14_6.png)
### Filtering merge requests by reviewer **(FREE)**
### Filtering merge requests by reviewer
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47605) in GitLab 13.7.
To filter review requested merge requests for a specific individual, you can type (or select from
the dropdown list) **Reviewer** and select the user.
### Filtering merge requests by environment or deployment date **(FREE)**
### Filtering merge requests by environment or deployment date
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44041) in GitLab 13.6.
@ -307,7 +308,7 @@ GitLab instance.
## Search settings
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292941) in GitLab 13.8 [with a flag](../../administration/feature_flags.md) named `search_settings_in_page`. Disabled by default.
> - [Added to Group, Administrator, and User settings](https://gitlab.com/groups/gitlab-org/-/epics/4842) in GitLab 13.9.
> - [Added](https://gitlab.com/groups/gitlab-org/-/epics/4842) to Group, Administrator, and User settings in GitLab 13.9.
> - [Feature flag `search_settings_in_page` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11.

View file

@ -37,8 +37,7 @@ You can create snippets in multiple ways, depending on whether you want to creat
**New snippet**, or:
- *If you're on a project's page,* select the plus icon (**{plus-square-o}**)
in the top navigation bar, and then select **New snippet** from the
**GitLab** (GitLab SaaS) or **Your Instance** (self-managed) section
of the same dropdown list.
**GitLab** section of the same dropdown list.
- *For all other pages,* select the plus icon (**{plus-square-o}**)
in the top navigation bar, then select **New snippet** from the dropdown list.
- If you installed the [GitLab Workflow VS Code extension](project/repository/vscode.md),
@ -153,8 +152,7 @@ To delete a file from your snippet through the GitLab UI:
1. Go to your snippet in the GitLab UI.
1. Select **Edit** in the top right corner.
1. Select **Delete file** alongside the filename of each file
you wish to delete.
1. Select **Delete file** alongside the filename of each file you wish to delete.
1. Select **Save changes**.
## Clone snippets

View file

@ -1773,13 +1773,13 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
msgid "AWS Access Key. Only required if not using role instance credentials"
msgstr ""
msgid "AWS Secret Access Key"
msgstr ""
msgid "AWS Secret Access Key. Only required if not using role instance credentials"
msgid "AWS access key ID (Optional)"
msgstr ""
msgid "AWS secret access key (Optional)"
msgstr ""
msgid "AWS service error: %{error}"
@ -1839,9 +1839,6 @@ msgstr ""
msgid "Access granted"
msgstr ""
msgid "Access key ID"
msgstr ""
msgid "Access requests"
msgstr ""
@ -19864,9 +19861,6 @@ msgstr ""
msgid "Integrations|Use default settings"
msgstr ""
msgid "Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled)."
msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
@ -20830,15 +20824,15 @@ msgstr ""
msgid "JiraService|Define the type of Jira issue to create from a vulnerability."
msgstr ""
msgid "JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used."
msgid "JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used."
msgstr ""
msgid "JiraService|Enable Jira issue creation from vulnerabilities"
msgstr ""
msgid "JiraService|Enable Jira issues"
msgstr ""
msgid "JiraService|Enable Jira issues creation from vulnerabilities"
msgstr ""
msgid "JiraService|Enable Jira transitions"
msgstr ""
@ -20953,16 +20947,19 @@ msgstr ""
msgid "JiraService|View Jira issues in GitLab"
msgstr ""
msgid "JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below."
msgid "JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select."
msgstr ""
msgid "JiraService|Web URL"
msgstr ""
msgid "JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only."
msgid "JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created."
msgstr ""
msgid "JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}."
msgid "JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues."
msgstr ""
msgid "JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}"
msgstr ""
msgid "Job"
@ -25611,6 +25608,9 @@ msgstr ""
msgid "Only reCAPTCHA v2 is supported:"
msgstr ""
msgid "Only required if not using role instance credentials."
msgstr ""
msgid "Only use lowercase letters, numbers, and underscores."
msgstr ""
@ -32290,9 +32290,6 @@ msgstr ""
msgid "Secret Detection"
msgstr ""
msgid "Secret access key"
msgstr ""
msgid "Secret token"
msgstr ""
@ -32491,9 +32488,6 @@ msgstr ""
msgid "SecurityOrchestration|.yaml preview"
msgstr ""
msgid "SecurityOrchestration|Action"
msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
@ -32536,9 +32530,6 @@ msgstr ""
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|Executes a %{scanType} scan"
msgstr ""
msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}."
msgstr ""
@ -32554,6 +32545,9 @@ msgstr ""
msgid "SecurityOrchestration|New policy"
msgstr ""
msgid "SecurityOrchestration|No actions defined - policy will not run."
msgstr ""
msgid "SecurityOrchestration|No description"
msgstr ""
@ -32596,10 +32590,13 @@ msgstr ""
msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:"
msgstr ""
msgid "SecurityOrchestration|Rule"
msgid "SecurityOrchestration|Rules"
msgstr ""
msgid "SecurityOrchestration|Rules"
msgid "SecurityOrchestration|Runs %{actions} and %{lastAction} scans"
msgstr ""
msgid "SecurityOrchestration|Runs a %{action} scan"
msgstr ""
msgid "SecurityOrchestration|Scan Execution"
@ -34688,10 +34685,10 @@ msgstr ""
msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance's code views and merge requests."
msgstr ""
msgid "SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph."
msgid "SourcegraphAdmin|Learn more."
msgstr ""
msgid "SourcegraphAdmin|More information"
msgid "SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph."
msgstr ""
msgid "SourcegraphAdmin|Save changes"
@ -34700,7 +34697,7 @@ msgstr ""
msgid "SourcegraphAdmin|Sourcegraph URL"
msgstr ""
msgid "SourcegraphAdmin|e.g. https://sourcegraph.example.com"
msgid "SourcegraphAdmin|https://sourcegraph.example.com"
msgstr ""
msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects."

38
scripts/ingest-reports-to-siem Executable file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env node
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3')
const { fromIni } = require('@aws-sdk/credential-provider-ini')
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
function getMD5HashFromFile(data) {
const hash = crypto.createHash('md5').update(data).digest('base64')
return hash
}
(async function () {
const s3Client = new S3Client({
region: 'us-east-2',
credentials: fromIni({ profile: 'gl-logs-for-panther' }),
})
try {
const file = 'gl-dependency-scanning-report.json'
const data = fs.readFileSync(file)
const responseData = await s3Client.send(
new PutObjectCommand({
Bucket: 'gl-logs-for-panther-test',
Key: path.join('package_hunter_test', path.basename(file)),
Body: data,
ContentMD5: getMD5HashFromFile(data),
}),
)
console.log('Successfully uploaded %s', file)
} catch (err) {
if (err.name === 'CredentialsProviderError' || err.name === 'AuthorizationHeaderMalformed')
console.log('Could not upload the report. Are AWS credentials configured in ~/.aws/credentials?')
else
console.log('Unexpected error during upload.')
process.exit(1)
}
})()

View file

@ -28,63 +28,25 @@ RSpec.describe ApplicationExperiment, :experiment do
end
describe "#publish" do
let(:should_track) { true }
before do
allow(application_experiment).to receive(:should_track?).and_return(should_track)
end
it "tracks the assignment", :snowplow do
expect(application_experiment).to receive(:track).with(:assignment)
application_experiment.publish
end
it "adds to the published experiments" do
# These are surfaced in the client layer by rendering them in the
# _published_experiments.html.haml partial.
application_experiment.publish
expect_snowplow_event(
category: 'namespaced/stub',
action: 'assignment',
context: [{ schema: anything, data: anything }]
expect(ApplicationExperiment.published_experiments['namespaced/stub']).to include(
experiment: 'namespaced/stub',
excluded: false,
key: anything,
variant: 'control'
)
end
it "publishes to the client" do
expect(application_experiment).to receive(:publish_to_client)
application_experiment.publish
end
context 'when we should not track' do
let(:should_track) { false }
it 'does not track an event to Snowplow', :snowplow do
application_experiment.publish
expect_no_snowplow_event
end
end
describe "#publish_to_client" do
it "adds the data into Gon" do
signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' }
expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => hash_including(signature) } }, true)
application_experiment.publish_to_client
end
it "handles when Gon raises exceptions (like when it can't be pushed into)" do
expect(Gon).to receive(:push).and_raise(NoMethodError)
expect { application_experiment.publish_to_client }.not_to raise_error
end
context 'when we should not track' do
let(:should_track) { false }
it 'returns early' do
expect(Gon).not_to receive(:push)
application_experiment.publish_to_client
end
end
end
describe '#publish_to_database' do
using RSpec::Parameterized::TableSyntax

View file

@ -40,10 +40,8 @@ RSpec.describe "User views incident" do
visit(project_issues_incident_path(project, incident))
end
it 'does not show the incident action', :js, :aggregate_failures do
click_button 'Incident actions'
expect(page).not_to have_link('New incident')
it 'does not show the incident actions', :js, :aggregate_failures do
expect(page).not_to have_button('Incident actions')
end
end
end

View file

@ -70,11 +70,6 @@ describe('RevokeButton', () => {
expect(findRevokeButton().exists()).toBe(true);
});
it('passes the buttonClass to the button', () => {
wrapper = createComponent({ buttonClass: 'my-revoke-button' });
expect(findRevokeButton().classes()).toContain('my-revoke-button');
});
it('opens the modal', () => {
findRevokeButton().trigger('click');
expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.modalId);

View file

@ -33,19 +33,19 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_runners_query_name = 'get_runners.query.graphql'
admin_runners_query = 'list/admin_runners.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runners_query_name}")
get_graphql_query_as_string("#{query_path}#{admin_runners_query}")
end
it "#{fixtures_path}#{get_runners_query_name}.json" do
it "#{fixtures_path}#{admin_runners_query}.json" do
post_graphql(query, current_user: admin, variables: {})
expect_graphql_errors_to_be_empty
end
it "#{fixtures_path}#{get_runners_query_name}.paginated.json" do
it "#{fixtures_path}#{admin_runners_query}.paginated.json" do
post_graphql(query, current_user: admin, variables: { first: 2 })
expect_graphql_errors_to_be_empty
@ -53,13 +53,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_runners_count_query_name = 'get_runners_count.query.graphql'
admin_runners_count_query = 'list/admin_runners_count.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runners_count_query_name}")
get_graphql_query_as_string("#{query_path}#{admin_runners_count_query}")
end
it "#{fixtures_path}#{get_runners_count_query_name}.json" do
it "#{fixtures_path}#{admin_runners_count_query}.json" do
post_graphql(query, current_user: admin, variables: {})
expect_graphql_errors_to_be_empty
@ -67,13 +67,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_runner_query_name = 'get_runner.query.graphql'
runner_query = 'details/runner.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_query_name}")
get_graphql_query_as_string("#{query_path}#{runner_query}")
end
it "#{fixtures_path}#{get_runner_query_name}.json" do
it "#{fixtures_path}#{runner_query}.json" do
post_graphql(query, current_user: admin, variables: {
id: instance_runner.to_global_id.to_s
})
@ -81,7 +81,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty
end
it "#{fixtures_path}#{get_runner_query_name}.with_group.json" do
it "#{fixtures_path}#{runner_query}.with_group.json" do
post_graphql(query, current_user: admin, variables: {
id: group_runner.to_global_id.to_s
})
@ -91,13 +91,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_runner_projects_query_name = 'get_runner_projects.query.graphql'
runner_projects_query = 'details/runner_projects.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_projects_query_name}")
get_graphql_query_as_string("#{query_path}#{runner_projects_query}")
end
it "#{fixtures_path}#{get_runner_projects_query_name}.json" do
it "#{fixtures_path}#{runner_projects_query}.json" do
post_graphql(query, current_user: admin, variables: {
id: project_runner.to_global_id.to_s
})
@ -107,13 +107,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_runner_jobs_query_name = 'get_runner_jobs.query.graphql'
runner_jobs_query = 'details/runner_jobs.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_jobs_query_name}")
get_graphql_query_as_string("#{query_path}#{runner_jobs_query}")
end
it "#{fixtures_path}#{get_runner_jobs_query_name}.json" do
it "#{fixtures_path}#{runner_jobs_query}.json" do
post_graphql(query, current_user: admin, variables: {
id: instance_runner.to_global_id.to_s
})
@ -131,13 +131,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_group_runners_query_name = 'get_group_runners.query.graphql'
group_runners_query = 'list/group_runners.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_group_runners_query_name}")
get_graphql_query_as_string("#{query_path}#{group_runners_query}")
end
it "#{fixtures_path}#{get_group_runners_query_name}.json" do
it "#{fixtures_path}#{group_runners_query}.json" do
post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path
})
@ -145,7 +145,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty
end
it "#{fixtures_path}#{get_group_runners_query_name}.paginated.json" do
it "#{fixtures_path}#{group_runners_query}.paginated.json" do
post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path,
first: 1
@ -156,13 +156,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end
describe GraphQL::Query, type: :request do
get_group_runners_count_query_name = 'get_group_runners_count.query.graphql'
group_runners_count_query = 'list/group_runners_count.query.graphql'
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_group_runners_count_query_name}")
get_graphql_query_as_string("#{query_path}#{group_runners_count_query}")
end
it "#{fixtures_path}#{get_group_runners_count_query_name}.json" do
it "#{fixtures_path}#{group_runners_count_query}.json" do
post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path
})

View file

@ -1,5 +1,5 @@
import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import { GlButton, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { mockTracking } from 'helpers/tracking_helper';
@ -65,12 +65,17 @@ describe('HeaderActions component', () => {
},
};
const findToggleIssueStateButton = () => wrapper.findComponent(GlButton);
const findDropdownAt = (index) => wrapper.findAllComponents(GlDropdown).at(index);
const findMobileDropdownItems = () => findDropdownAt(0).findAllComponents(GlDropdownItem);
const findDesktopDropdownItems = () => findDropdownAt(1).findAllComponents(GlDropdownItem);
const findModal = () => wrapper.findComponent(GlModal);
const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index);
const findToggleIssueStateButton = () => wrapper.find(GlButton);
const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`);
const findMobileDropdown = () => findDropdownBy('mobile-dropdown');
const findDesktopDropdown = () => findDropdownBy('desktop-dropdown');
const findMobileDropdownItems = () => findMobileDropdown().findAll(GlDropdownItem);
const findDesktopDropdownItems = () => findDesktopDropdown().findAll(GlDropdownItem);
const findModal = () => wrapper.find(GlModal);
const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index);
const mountComponent = ({
props = {},
@ -161,10 +166,10 @@ describe('HeaderActions component', () => {
});
describe.each`
description | isCloseIssueItemVisible | findDropdownItems
${'mobile dropdown'} | ${true} | ${findMobileDropdownItems}
${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems}
`('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => {
description | isCloseIssueItemVisible | findDropdownItems | findDropdown
${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} | ${findMobileDropdown}
${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} | ${findDesktopDropdown}
`('$description', ({ isCloseIssueItemVisible, findDropdownItems, findDropdown }) => {
describe.each`
description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue
${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
@ -214,6 +219,24 @@ describe('HeaderActions component', () => {
});
},
);
describe(`when user can update but not create ${issueType}`, () => {
beforeEach(() => {
wrapper = mountComponent({
props: {
canUpdateIssue: true,
canCreateIssue: false,
isIssueAuthor: true,
issueType,
canReportSpam: false,
canPromoteToEpic: false,
},
});
});
it(`${isCloseIssueItemVisible ? 'shows' : 'hides'} the dropdown button`, () => {
expect(findDropdown().exists()).toBe(isCloseIssueItemVisible);
});
});
});
});

View file

@ -1,7 +1,7 @@
import { GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
import InlineConflictLines from '~/merge_conflicts/components/inline_conflict_lines.vue';
import ParallelConflictLines from '~/merge_conflicts/components/parallel_conflict_lines.vue';
import component from '~/merge_conflicts/merge_conflict_resolver_app.vue';
@ -18,7 +18,7 @@ describe('Merge Conflict Resolver App', () => {
const decoratedMockFiles = decorateFiles(conflictsMock.files);
const mountComponent = () => {
wrapper = shallowMount(component, {
wrapper = shallowMountExtended(component, {
store,
stubs: { GlSprintf },
provide() {
@ -41,15 +41,17 @@ describe('Merge Conflict Resolver App', () => {
wrapper.destroy();
});
const findConflictsCount = () => wrapper.find('[data-testid="conflicts-count"]');
const findFiles = () => wrapper.findAll('[data-testid="files"]');
const findFileHeader = (w = wrapper) => w.find('[data-testid="file-name"]');
const findFileInteractiveButton = (w = wrapper) => w.find('[data-testid="interactive-button"]');
const findFileInlineButton = (w = wrapper) => w.find('[data-testid="inline-button"]');
const findSideBySideButton = () => wrapper.find('[data-testid="side-by-side"]');
const findLoadingSpinner = () => wrapper.findByTestId('loading-spinner');
const findConflictsCount = () => wrapper.findByTestId('conflicts-count');
const findFiles = () => wrapper.findAllByTestId('files');
const findFileHeader = (w = wrapper) => extendedWrapper(w).findByTestId('file-name');
const findFileInteractiveButton = (w = wrapper) =>
extendedWrapper(w).findByTestId('interactive-button');
const findFileInlineButton = (w = wrapper) => extendedWrapper(w).findByTestId('inline-button');
const findSideBySideButton = () => wrapper.findByTestId('side-by-side');
const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines);
const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines);
const findCommitMessageTextarea = () => wrapper.find('[data-testid="commit-message"]');
const findCommitMessageTextarea = () => wrapper.findByTestId('commit-message');
it('shows the amount of conflicts', () => {
mountComponent();
@ -60,6 +62,19 @@ describe('Merge Conflict Resolver App', () => {
expect(title.text().trim()).toBe('Showing 3 conflicts between test-conflicts and main');
});
it('shows a loading spinner while loading', () => {
store.commit('SET_LOADING_STATE', true);
mountComponent();
expect(findLoadingSpinner().exists()).toBe(true);
});
it('does not show a loading spinner once loaded', () => {
mountComponent();
expect(findLoadingSpinner().exists()).toBe(false);
});
describe('files', () => {
it('shows one file area for each file', () => {
mountComponent();

View file

@ -18,12 +18,15 @@ import {
COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT,
COMMIT_FAILURE,
EDITOR_APP_STATUS_LOADING,
} from '~/pipeline_editor/constants';
import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql';
import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql';
import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
@ -84,9 +87,6 @@ describe('Pipeline editor app component', () => {
initialCiFileContent: {
loading: blobLoading,
},
ciConfigData: {
loading: false,
},
},
},
},
@ -94,7 +94,11 @@ describe('Pipeline editor app component', () => {
});
};
const createComponentWithApollo = async ({ provide = {}, stubs = {} } = {}) => {
const createComponentWithApollo = async ({
provide = {},
stubs = {},
withUndefinedBranch = false,
} = {}) => {
const handlers = [
[getBlobContent, mockBlobContentData],
[getCiConfigData, mockCiConfigData],
@ -105,6 +109,31 @@ describe('Pipeline editor app component', () => {
mockApollo = createMockApollo(handlers, resolvers);
if (!withUndefinedBranch) {
mockApollo.clients.defaultClient.cache.writeQuery({
query: getCurrentBranch,
data: {
workBranches: {
__typename: 'BranchList',
current: {
__typename: 'WorkBranch',
name: mockDefaultBranch,
},
},
},
});
}
mockApollo.clients.defaultClient.cache.writeQuery({
query: getAppStatus,
data: {
app: {
__typename: 'AppData',
status: EDITOR_APP_STATUS_LOADING,
},
},
});
const options = {
localVue,
mocks: {},
@ -145,6 +174,55 @@ describe('Pipeline editor app component', () => {
});
});
describe('skipping queries', () => {
describe('when branchName is undefined', () => {
beforeEach(async () => {
await createComponentWithApollo({ withUndefinedBranch: true });
});
it('does not calls getBlobContent', () => {
expect(mockBlobContentData).not.toHaveBeenCalled();
});
});
describe('when branchName is defined', () => {
beforeEach(async () => {
await createComponentWithApollo();
});
it('calls getBlobContent', () => {
expect(mockBlobContentData).toHaveBeenCalled();
});
});
describe('when commit sha is undefined', () => {
beforeEach(async () => {
mockLatestCommitShaQuery.mockResolvedValue(undefined);
await createComponentWithApollo();
});
it('calls getBlobContent', () => {
expect(mockBlobContentData).toHaveBeenCalled();
});
it('does not call ciConfigData', () => {
expect(mockCiConfigData).not.toHaveBeenCalled();
});
});
describe('when commit sha is defined', () => {
beforeEach(async () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
await createComponentWithApollo();
});
it('calls ciConfigData', () => {
expect(mockCiConfigData).toHaveBeenCalled();
});
});
});
describe('when queries are called', () => {
beforeEach(() => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);

View file

@ -7,7 +7,7 @@ import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import runnerQuery from '~/runner/graphql/details/runner.query.graphql';
import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils';
@ -29,7 +29,7 @@ describe('AdminRunnerEditApp', () => {
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(AdminRunnerEditApp, {
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]),
apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]),
propsData: {
runnerId: mockRunnerId,
...props,

View file

@ -9,7 +9,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue';
import RunnerPauseButton from '~/runner/components/runner_pause_button.vue';
import RunnerEditButton from '~/runner/components/runner_edit_button.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import runnerQuery from '~/runner/graphql/details/runner.query.graphql';
import AdminRunnerShowApp from '~/runner/admin_runner_show/admin_runner_show_app.vue';
import { captureException } from '~/runner/sentry_utils';
@ -42,7 +42,7 @@ describe('AdminRunnerShowApp', () => {
const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(AdminRunnerShowApp, {
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]),
apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]),
propsData: {
runnerId: mockRunnerId,
...props,

View file

@ -35,8 +35,8 @@ import {
STATUS_ACTIVE,
RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
import getRunnersCountQuery from '~/runner/graphql/get_runners_count.query.graphql';
import adminRunnersQuery from '~/runner/graphql/list/admin_runners.query.graphql';
import adminRunnersCountQuery from '~/runner/graphql/list/admin_runners_count.query.graphql';
import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
@ -71,8 +71,8 @@ describe('AdminRunnersApp', () => {
const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
const handlers = [
[getRunnersQuery, mockRunnersQuery],
[getRunnersCountQuery, mockRunnersCountQuery],
[adminRunnersQuery, mockRunnersQuery],
[adminRunnersCountQuery, mockRunnersCountQuery],
];
wrapper = mountFn(AdminRunnersApp, {

View file

@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql';
import { captureException } from '~/runner/sentry_utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';

View file

@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql';
import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql';
import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/runner/sentry_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';

View file

@ -11,7 +11,7 @@ import RunnerPagination from '~/runner/components/runner_pagination.vue';
import { captureException } from '~/runner/sentry_utils';
import { I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '~/runner/constants';
import getRunnerJobsQuery from '~/runner/graphql/get_runner_jobs.query.graphql';
import runnerJobsQuery from '~/runner/graphql/details/runner_jobs.query.graphql';
import { runnerData, runnerJobsData } from '../mock_data';
@ -34,7 +34,7 @@ describe('RunnerJobs', () => {
const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
wrapper = mountFn(RunnerJobs, {
apolloProvider: createMockApollo([[getRunnerJobsQuery, mockRunnerJobsQuery]]),
apolloProvider: createMockApollo([[runnerJobsQuery, mockRunnerJobsQuery]]),
propsData: {
runner: mockRunner,
},

View file

@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql';
import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql';
import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/runner/sentry_utils';
import { createAlert } from '~/flash';

View file

@ -16,7 +16,7 @@ import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue';
import RunnerPagination from '~/runner/components/runner_pagination.vue';
import { captureException } from '~/runner/sentry_utils';
import getRunnerProjectsQuery from '~/runner/graphql/get_runner_projects.query.graphql';
import runnerProjectsQuery from '~/runner/graphql/details/runner_projects.query.graphql';
import { runnerData, runnerProjectsData } from '../mock_data';
@ -40,7 +40,7 @@ describe('RunnerProjects', () => {
const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
wrapper = mountFn(RunnerProjects, {
apolloProvider: createMockApollo([[getRunnerProjectsQuery, mockRunnerProjectsQuery]]),
apolloProvider: createMockApollo([[runnerProjectsQuery, mockRunnerProjectsQuery]]),
propsData: {
runner: mockRunner,
},

View file

@ -13,7 +13,7 @@ import {
ACCESS_LEVEL_REF_PROTECTED,
ACCESS_LEVEL_NOT_PROTECTED,
} from '~/runner/constants';
import runnerUpdateMutation from '~/runner/graphql/runner_update.mutation.graphql';
import runnerUpdateMutation from '~/runner/graphql/details/runner_update.mutation.graphql';
import { captureException } from '~/runner/sentry_utils';
import { runnerData } from '../mock_data';

View file

@ -32,8 +32,8 @@ import {
RUNNER_PAGE_SIZE,
I18N_EDIT,
} from '~/runner/constants';
import getGroupRunnersQuery from '~/runner/graphql/get_group_runners.query.graphql';
import getGroupRunnersCountQuery from '~/runner/graphql/get_group_runners_count.query.graphql';
import getGroupRunnersQuery from '~/runner/graphql/list/group_runners.query.graphql';
import getGroupRunnersCountQuery from '~/runner/graphql/list/group_runners_count.query.graphql';
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';

View file

@ -1,18 +1,18 @@
// Fixtures generated by: spec/frontend/fixtures/runner.rb
// Admin queries
import runnersData from 'test_fixtures/graphql/runner/get_runners.query.graphql.json';
import runnersCountData from 'test_fixtures/graphql/runner/get_runners_count.query.graphql.json';
import runnersDataPaginated from 'test_fixtures/graphql/runner/get_runners.query.graphql.paginated.json';
import runnerData from 'test_fixtures/graphql/runner/get_runner.query.graphql.json';
import runnerWithGroupData from 'test_fixtures/graphql/runner/get_runner.query.graphql.with_group.json';
import runnerProjectsData from 'test_fixtures/graphql/runner/get_runner_projects.query.graphql.json';
import runnerJobsData from 'test_fixtures/graphql/runner/get_runner_jobs.query.graphql.json';
// List queries
import runnersData from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.json';
import runnersDataPaginated from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.paginated.json';
import runnersCountData from 'test_fixtures/graphql/runner/list/admin_runners_count.query.graphql.json';
import groupRunnersData from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.json';
import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.paginated.json';
import groupRunnersCountData from 'test_fixtures/graphql/runner/list/group_runners_count.query.graphql.json';
// Group queries
import groupRunnersData from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.json';
import groupRunnersCountData from 'test_fixtures/graphql/runner/get_group_runners_count.query.graphql.json';
import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.paginated.json';
// Details queries
import runnerData from 'test_fixtures/graphql/runner/details/runner.query.graphql.json';
import runnerWithGroupData from 'test_fixtures/graphql/runner/details/runner.query.graphql.with_group.json';
import runnerProjectsData from 'test_fixtures/graphql/runner/details/runner_projects.query.graphql.json';
import runnerJobsData from 'test_fixtures/graphql/runner/details/runner_jobs.query.graphql.json';
export {
runnersData,

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DeployTokensHelper do
describe '#deploy_token_revoke_button_data' do
let_it_be(:token) { build(:deploy_token) }
let_it_be(:project) { build(:project) }
let_it_be(:revoke_deploy_token_path) { '/foobar/baz/-/deploy_tokens/1/revoke' }
it 'returns expected hash' do
expect(helper).to receive(:revoke_deploy_token_path).with(project, token).and_return(revoke_deploy_token_path)
expect(helper.deploy_token_revoke_button_data(token: token, group_or_project: project)).to match({
token: token.to_json(only: [:id, :name]),
revoke_path: revoke_deploy_token_path
})
end
end
end

View file

@ -49,7 +49,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject
it 'invalidates the markdown cache of related projects' do
expect(subject).to receive(:remove_cached_html_for_projects)
.with(projects.map(&:id))
.with(a_collection_containing_exactly(*projects.map(&:id)))
subject.rename_projects
end

View file

@ -390,26 +390,8 @@ RSpec.describe Group do
let!(:old_parent) { create(:group, parent: root) }
let!(:new_parent) { create(:group, parent: root) }
context 'with FOR UPDATE lock' do
before do
stub_feature_flags(for_no_key_update_lock: false)
subject
reload_models(old_parent, new_parent, group)
end
it 'updates traversal_ids' do
expect(group.traversal_ids).to eq [root.id, new_parent.id, group.id]
end
it_behaves_like 'hierarchy with traversal_ids'
it_behaves_like 'locked row', 'FOR UPDATE' do
let(:row) { root }
end
end
context 'with FOR NO KEY UPDATE lock' do
before do
stub_feature_flags(for_no_key_update_lock: true)
subject
reload_models(old_parent, new_parent, group)
end
@ -419,7 +401,7 @@ RSpec.describe Group do
end
it_behaves_like 'hierarchy with traversal_ids'
it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do
it_behaves_like 'locked row' do
let(:row) { root }
end
end

View file

@ -68,24 +68,11 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model do
end
end
it_behaves_like 'locked row', 'FOR UPDATE' do
it_behaves_like 'locked row' do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
let(:row) { root }
before do
stub_feature_flags(for_no_key_update_lock: false)
recorded_queries.record { subject }
end
end
it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
let(:row) { root }
before do
stub_feature_flags(for_no_key_update_lock: true)
recorded_queries.record { subject }
end
end

View file

@ -436,17 +436,7 @@ RSpec.describe Namespace do
it { expect(namespace.traversal_ids).to eq [namespace.id] }
end
context 'with before_commit callback' do
it_behaves_like 'default traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'default traversal_ids'
end
it_behaves_like 'default traversal_ids'
end
describe "after_commit :expire_child_caches" do

View file

@ -262,17 +262,7 @@ RSpec.describe Project, factory_default: :keep do
end
end
context 'sync-ing traversal_ids in before_commit callback' do
it_behaves_like 'creates project namespace'
end
context 'sync-ing traversal_ids in after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'creates project namespace'
end
it_behaves_like 'creates project namespace'
end
end

View file

@ -85,14 +85,6 @@ RSpec.describe Groups::CreateService, '#execute' do
context 'with before_commit callback' do
it_behaves_like 'has sync-ed traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
end
context 'when user can not create a group' do
@ -119,17 +111,7 @@ RSpec.describe Groups::CreateService, '#execute' do
expect { subject }.not_to change(OnboardingProgress, :count).from(0)
end
context 'with before_commit callback' do
it_behaves_like 'has sync-ed traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
it_behaves_like 'has sync-ed traversal_ids'
end
context 'as guest' do

View file

@ -225,17 +225,7 @@ RSpec.describe Projects::CreateService, '#execute' do
expect(project.project_namespace).to be_in_sync_with_project(project)
end
context 'with before_commit callback' do
it_behaves_like 'has sync-ed traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
it_behaves_like 'has sync-ed traversal_ids'
end
context 'group sharing', :sidekiq_inline do

View file

@ -49,6 +49,7 @@ module SimpleCovEnv
def configure_profile
SimpleCov.configure do
enable_coverage :branch
load_profile 'test_frameworks'
track_files '{app,config/initializers,config/initializers_before_autoloader,db/post_migrate,haml_lint,lib,rubocop,tooling}/**/*.rb'

View file

@ -4,10 +4,10 @@
# Ensure a transaction also occurred.
# Be careful! This form of spec is not foolproof, but better than nothing.
RSpec.shared_examples 'locked row' do |lock_type|
RSpec.shared_examples 'locked row' do
it "has locked row" do
table_name = row.class.table_name
ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+#{lock_type}/m
ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+FOR NO KEY UPDATE/m
expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
expect(recorded_queries.log).to include a_string_matching ids_regex

View file

@ -16,8 +16,8 @@ RSpec.describe 'admin/application_settings/_eks' do
shared_examples 'EKS secret access key input' do
it 'renders an empty password field' do
render
expect(rendered).to have_field('Secret access key', type: 'password')
expect(page.find_field('Secret access key').value).to be_blank
expect(rendered).to have_field('AWS secret access key (Optional)', type: 'password')
expect(page.find_field('AWS secret access key (Optional)').value).to be_blank
end
end

View file

@ -4,22 +4,20 @@ require 'spec_helper'
RSpec.describe 'layouts/_published_experiments', :experiment do
before do
stub_const('TestControlExperiment', ApplicationExperiment)
stub_const('TestCandidateExperiment', ApplicationExperiment)
stub_const('TestExcludedExperiment', ApplicationExperiment)
# Stub each experiment to be enabled, otherwise tracking does not happen.
stub_experiments(
test_control: :control,
test_excluded: true,
test_published_only: :control,
test_candidate: :candidate,
test_variant: :variant_name
)
TestControlExperiment.new('test_control').tap do |e|
e.variant(:control)
e.publish
end
TestCandidateExperiment.new('test_candidate').tap do |e|
e.variant(:candidate)
e.publish
end
TestExcludedExperiment.new('test_excluded').tap do |e|
e.exclude!
e.publish
end
experiment(:test_control) { }
experiment(:test_excluded) { |e| e.exclude! }
experiment(:test_candidate) { |e| e.candidate { } }
experiment(:test_variant) { |e| e.variant(:variant_name) { } }
experiment(:test_published_only).publish
render
end
@ -29,7 +27,9 @@ RSpec.describe 'layouts/_published_experiments', :experiment do
expect(output).to include('gl.experiments = {')
expect(output).to match(/"test_control":\{[^}]*"variant":"control"/)
expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/)
expect(output).not_to include('"test_excluded"')
expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/)
expect(output).to match(/"test_variant":\{[^}]*"variant":"variant_name"/)
expect(output).to match(/"test_published_only":\{[^}]*"variant":"control"/)
end
end