Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-15 15:09:20 +00:00
parent 3e0c035fe3
commit 9440c17f55
94 changed files with 923 additions and 627 deletions

View File

@ -44,6 +44,7 @@ workflow:
# For the 2-hourly scheduled pipelines, we set specific variables.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "2-hourly"'
variables:
RUBY_VERSION: "3.0"
CRYSTALBALL: "true"
# For `$CI_DEFAULT_BRANCH` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
@ -59,7 +60,7 @@ workflow:
variables:
PG_VERSION: "12"
DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-2.7.patched-golang-1.17-node-16.14-postgresql-${PG_VERSION}:git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-yarn-1.22-graphicsmagick-1.3.36"
DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}.patched-golang-1.17-node-16.14-postgresql-${PG_VERSION}:git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-yarn-1.22-graphicsmagick-1.3.36"
RAILS_ENV: "test"
NODE_ENV: "test"
BUNDLE_WITHOUT: "production:development"
@ -75,6 +76,7 @@ variables:
DEBIAN_VERSION: "bullseye"
CHROME_VERSION: "101"
DOCKER_VERSION: "20.10.14"
RUBY_VERSION: "2.7"
TMP_TEST_FOLDER: "${CI_PROJECT_DIR}/tmp/tests"
GITLAB_WORKHORSE_FOLDER: "gitlab-workhorse"

View File

@ -2,7 +2,7 @@
extends:
- .default-retry
- .docs:rules:review-docs
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}-alpine
stage: review
needs: []
variables:

View File

@ -11,7 +11,7 @@
- .default-retry
- .default-before_script
- .assets-compile-cache
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-2.7-git-2.33-lfs-2.9-node-16.14-yarn-1.22-graphicsmagick-1.3.36
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-git-2.33-lfs-2.9-node-16.14-yarn-1.22-graphicsmagick-1.3.36
variables:
SETUP_DB: "false"
WEBPACK_VENDOR_DLL: "true"

View File

@ -18,7 +18,7 @@
- source scripts/prepare_build.sh
.ruby-gems-cache: &ruby-gems-cache
key: "ruby-gems-${DEBIAN_VERSION}"
key: "ruby-gems-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
paths:
- vendor/ruby/
policy: pull
@ -28,7 +28,7 @@
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.gitaly-ruby-gems-cache: &gitaly-ruby-gems-cache
key: "gitaly-ruby-gems-${DEBIAN_VERSION}"
key: "gitaly-ruby-gems-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
paths:
- vendor/gitaly-ruby/
policy: pull
@ -42,7 +42,7 @@
files:
- GITALY_SERVER_VERSION
- lib/gitlab/setup_helper.rb
prefix: "gitaly-binaries-${DEBIAN-VERSION}"
prefix: "gitaly-binaries-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
paths:
- ${TMP_TEST_FOLDER}/gitaly/_build/bin/
- ${TMP_TEST_FOLDER}/gitaly/_build/deps/git/install/
@ -79,7 +79,7 @@
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.assets-cache: &assets-cache
key: "assets-${DEBIAN_VERSION}-${NODE_ENV}"
key: "assets-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-node-${NODE_ENV}"
paths:
- assets-hash.txt
- public/assets/webpack/
@ -103,7 +103,7 @@
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.rubocop-cache: &rubocop-cache
key: "rubocop-${DEBIAN_VERSION}"
key: "rubocop-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
paths:
- tmp/rubocop_cache/
policy: pull
@ -116,6 +116,7 @@
.qa-ruby-gems-cache: &qa-ruby-gems-cache
key:
prefix: "qa-ruby-gems-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
files:
- qa/Gemfile.lock
paths:

View File

@ -92,7 +92,7 @@ populate-qa-tests-var:
- detect-tests
.package-and-qa-base:
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}-alpine
stage: qa
retry: 0
before_script:

View File

@ -395,15 +395,15 @@ db:migrate-from-previous-major-version:
USE_BUNDLE_INSTALL: "false"
SETUP_DB: "false"
PROJECT_TO_CHECKOUT: "gitlab-foss"
TAG_TO_CHECKOUT: "v13.12.9"
TAG_TO_CHECKOUT: "v14.10.2"
before_script:
- !reference [.default-before_script, before_script]
- '[[ -d "ee/" ]] || export PROJECT_TO_CHECKOUT="gitlab"'
- '[[ -d "ee/" ]] || export TAG_TO_CHECKOUT="${TAG_TO_CHECKOUT}-ee"'
- retry 'git fetch https://gitlab.com/gitlab-org/$PROJECT_TO_CHECKOUT.git $TAG_TO_CHECKOUT'
- git checkout -f FETCH_HEAD
- SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh
- run_timed_command "bundle exec rake db:drop db:create db:structure:load db:migrate db:seed_fu"
- SETUP_DB=false USE_BUNDLE_INSTALL=true ENABLE_BOOTSNAP=false bash scripts/prepare_build.sh
- run_timed_command "ENABLE_BOOTSNAP=false bundle exec rake db:drop db:create db:structure:load db:migrate db:seed_fu"
- git checkout -f $CI_COMMIT_SHA
- SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh
script:
@ -419,7 +419,7 @@ db:migrate-from-previous-major-version-single-db:
extends:
- .rails:rules:ee-mr-and-default-branch-only
variables:
TAG_TO_CHECKOUT: "v14.4.0"
TAG_TO_CHECKOUT: "v14.7.0" # this version updated grpc to 1.42.0, which supports Ruby 2 & 3
script:
- run_timed_command "scripts/db_tasks db:migrate"
- scripts/schema_changed.sh
@ -460,7 +460,7 @@ db:migrate-non-superuser:
db:gitlabcom-database-testing:
extends: .rails:rules:db:gitlabcom-database-testing
stage: test
image: ruby:2.7-alpine
image: ruby:${RUBY_VERSION}-alpine
needs: []
allow_failure: true
script:
@ -976,7 +976,6 @@ rspec system pg13:
- .rspec-base-pg13
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-system-parallel
# EE/FOSS: default branch nightly scheduled jobs #
##########################################

View File

@ -20,7 +20,7 @@ review-build-cng-env:
extends:
- .default-retry
- .review:rules:review-build-cng
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine3.13
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}-alpine3.13
stage: prepare
needs: []
before_script:

View File

@ -60,7 +60,7 @@ no-jh-check:
verify-tests-yml:
extends:
- .setup:rules:verify-tests-yml
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine3.13
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}-alpine3.13
stage: test
needs: []
script:
@ -96,7 +96,7 @@ generate-frontend-fixtures-mapping:
- ${FRONTEND_FIXTURES_MAPPING_PATH}
.detect-test-base:
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
needs: []
stage: prepare
script:
@ -160,7 +160,7 @@ detect-previous-failed-tests:
add-jh-folder:
extends: .setup:rules:add-jh-folder
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
stage: prepare
before_script:
- source ./scripts/utils.sh

View File

@ -1,5 +1,5 @@
.tests-metadata-state:
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
before_script:
- source scripts/utils.sh
artifacts:

View File

@ -22,4 +22,4 @@ workhorse:verify:
workhorse:test using go 1.17:
extends: .workhorse:test
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-2.7-golang-1.17-git-2.31
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-golang-1.17-git-2.31

View File

@ -1 +1 @@
9f87cdc737619e33cddc3ceddc2103dc11dd2577
13086e25394b65e4c17eca8484890f62bb2f0b92

View File

@ -47,8 +47,8 @@ export default {
computed: {
icons() {
return this.visible
? { caret: 'angle-down', folder: 'folder-open' }
: { caret: 'angle-right', folder: 'folder-o' };
? { caret: 'chevron-lg-down', folder: 'folder-open' }
: { caret: 'chevron-lg-right', folder: 'folder-o' };
},
label() {
return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand;

View File

@ -37,9 +37,7 @@ function monkeyPatchConfirmModal() {
Rails.confirm = confirmViaModal;
}
if (gon?.features?.bootstrapConfirmationModals) {
monkeyPatchConfirmModal();
}
monkeyPatchConfirmModal();
export const initRails = () => {
// eslint-disable-next-line no-underscore-dangle

View File

@ -1,42 +1,118 @@
<script>
import { GlAvatar, GlLink } from '@gitlab/ui';
import { GlTokenSelector, GlIcon, GlAvatar, GlLink } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import localUpdateWorkItemMutation from '../graphql/local_update_work_item.mutation.graphql';
function isClosingIcon(el) {
return el?.classList.contains('gl-token-close');
}
export default {
components: {
GlTokenSelector,
GlIcon,
GlAvatar,
GlLink,
},
props: {
workItemId: {
type: String,
required: true,
},
assignees: {
type: Array,
required: true,
},
},
data() {
return {
isEditing: false,
localAssignees: this.assignees.map((assignee) => ({
...assignee,
class: 'gl-bg-transparent!',
})),
};
},
computed: {
assigneeIds() {
return this.localAssignees.map((assignee) => assignee.id);
},
assigneeListEmpty() {
return this.assignees.length === 0;
},
containerClass() {
return !this.isEditing ? 'gl-shadow-none! gl-bg-transparent!' : '';
},
},
methods: {
getUserId(id) {
return getIdFromGraphQLId(id);
},
setAssignees(e) {
if (isClosingIcon(e.relatedTarget) || !this.isEditing) return;
this.isEditing = false;
this.$apollo.mutate({
mutation: localUpdateWorkItemMutation,
variables: {
input: {
id: this.workItemId,
assigneeIds: this.assigneeIds,
},
},
});
},
async focusTokenSelector() {
this.isEditing = true;
await this.$nextTick();
this.$refs.tokenSelector.focusTextInput();
},
},
};
</script>
<template>
<div class="gl-display-flex">
<span class="gl-font-weight-bold gl-w-15 gl-pt-1">{{ __('Assignee(s)') }}</span>
<div class="gl-mb-4">
<gl-link
v-for="user in assignees"
:key="user.id"
:href="user.webUrl"
:title="`test`"
:data-user-id="getUserId(user.id)"
data-placement="top"
class="gl-text-decoration-none! gl-text-black-normal! gl-display-flex gl-md-display-inline-flex! gl-align-items-center gl-mb-4 gl-md-mb-0 gl-mr-4 js-user-link"
>
<gl-avatar :size="24" :src="user.avatarUrl" />
<span class="gl-pl-2">{{ user.name }}</span>
</gl-link>
<div class="gl-display-flex gl-mb-4 work-item-assignees gl-relative">
<span class="gl-font-weight-bold gl-w-15 gl-pt-2" data-testid="assignees-title">{{
__('Assignee(s)')
}}</span>
<!-- TODO: Remove this div when https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2872 is merged -->
<div
v-if="assigneeListEmpty && !isEditing"
class="add-assignees gl-min-w-fit-content gl-absolute gl-display-flex gl-align-items-center gl-text-gray-300 gl-pr-4 gl-top-2 gl-z-index-0"
data-testid="empty-state"
>
<gl-icon name="profile" />
<span class="gl-ml-2">{{ __('Add assignees') }}</span>
</div>
<gl-token-selector
ref="tokenSelector"
v-model="localAssignees"
hide-dropdown-with-no-items
:container-class="containerClass"
class="gl-w-full gl-border gl-border-white gl-hover-border-gray-200 gl-rounded-base gl-z-index-1 gl-bg-transparent!"
@token-remove="focusTokenSelector"
@focus="isEditing = true"
@blur="setAssignees"
>
<template #token-content="{ token }">
<gl-link
:href="token.webUrl"
:title="token.name"
:data-user-id="getUserId(token.id)"
data-placement="top"
class="gl-text-decoration-none! gl-text-body! gl-display-flex gl-md-display-inline-flex! gl-align-items-center js-user-link"
>
<gl-avatar :size="24" :src="token.avatarUrl" />
<span class="gl-pl-2">{{ token.name }}</span>
</gl-link>
</template>
</gl-token-selector>
</div>
</template>
<style lang="scss">
/* TODO: Remove style block when https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2872 is merged */
.work-item-assignees .add-assignees {
left: 7.5rem;
}
</style>

View File

@ -133,7 +133,11 @@ export default {
/>
</div>
<template v-if="workItemsMvc2Enabled">
<work-item-assignees v-if="workItemAssignees" :assignees="workItemAssignees.nodes" />
<work-item-assignees
v-if="workItemAssignees"
:work-item-id="workItem.id"
:assignees="workItemAssignees.nodes"
/>
<work-item-weight v-if="workItemWeight" :weight="workItemWeight.weight" />
</template>
<work-item-state

View File

@ -0,0 +1,9 @@
#import "./work_item.fragment.graphql"
mutation localUpdateWorkItem($input: LocalWorkItemAssigneesInput) {
localUpdateWorkItem(input: $input) @client {
workItem {
...WorkItem
}
}
}

View File

@ -1,7 +1,10 @@
import produce from 'immer';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { WIDGET_TYPE_ASSIGNEE } from '../constants';
import typeDefs from './typedefs.graphql';
import workItemQuery from './work_item.query.graphql';
export const temporaryConfig = {
typeDefs,
@ -13,38 +16,40 @@ export const temporaryConfig = {
WorkItem: {
fields: {
mockWidgets: {
read() {
return [
{
__typename: 'LocalWorkItemAssignees',
type: 'ASSIGNEES',
nodes: [
{
__typename: 'UserCore',
id: 'gid://gitlab/User/1',
avatarUrl: '',
webUrl: '',
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'John Doe',
username: 'doe_I',
},
{
__typename: 'UserCore',
id: 'gid://gitlab/User/2',
avatarUrl: '',
webUrl: '',
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Marcus Rutherford',
username: 'ruthfull',
},
],
},
{
__typename: 'LocalWorkItemWeight',
type: 'WEIGHT',
weight: 0,
},
];
read(widgets) {
return (
widgets || [
{
__typename: 'LocalWorkItemAssignees',
type: 'ASSIGNEES',
nodes: [
{
__typename: 'UserCore',
id: 'gid://gitlab/User/1',
avatarUrl: '',
webUrl: '',
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'John Doe',
username: 'doe_I',
},
{
__typename: 'UserCore',
id: 'gid://gitlab/User/2',
avatarUrl: '',
webUrl: '',
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Marcus Rutherford',
username: 'ruthfull',
},
],
},
{
__typename: 'LocalWorkItemWeight',
type: 'WEIGHT',
weight: 0,
},
]
);
},
},
},
@ -53,10 +58,36 @@ export const temporaryConfig = {
},
};
export const resolvers = {
Mutation: {
localUpdateWorkItem(_, { input }, { cache }) {
const sourceData = cache.readQuery({
query: workItemQuery,
variables: { id: input.id },
});
const data = produce(sourceData, (draftData) => {
const assigneesWidget = draftData.workItem.mockWidgets.find(
(widget) => widget.type === WIDGET_TYPE_ASSIGNEE,
);
assigneesWidget.nodes = assigneesWidget.nodes.filter((assignee) =>
input.assigneeIds.includes(assignee.id),
);
});
cache.writeQuery({
query: workItemQuery,
variables: { id: input.id },
data,
});
},
},
};
export function createApolloProvider() {
Vue.use(VueApollo);
const defaultClient = createDefaultClient({}, temporaryConfig);
const defaultClient = createDefaultClient(resolvers, temporaryConfig);
return new VueApollo({
defaultClient,

View File

@ -20,3 +20,17 @@ type LocalWorkItemWeight implements LocalWorkItemWidget {
extend type WorkItem {
mockWidgets: [LocalWorkItemWidget]
}
type LocalWorkItemAssigneesInput {
id: WorkItemID!
assigneeIds: [ID!]
}
type LocalWorkItemPayload {
workItem: WorkItem!
errors: [String!]
}
extend type Mutation {
localUpdateWorkItem(input: LocalWorkItemAssigneesInput!): LocalWorkItemPayload
}

View File

@ -15,7 +15,7 @@ class ErrorTracking::ErrorEvent < ApplicationRecord
validates :occurred_at, presence: true
def stacktrace
@stacktrace ||= build_stacktrace
@stacktrace ||= ErrorTracking::StacktraceBuilder.new(payload).stacktrace
end
# For compatibility with sentry integration
@ -30,56 +30,4 @@ class ErrorTracking::ErrorEvent < ApplicationRecord
def release
payload.dig('release')
end
private
def build_stacktrace
raw_stacktrace = find_stacktrace_from_payload
return [] unless raw_stacktrace
raw_stacktrace.map do |entry|
{
'lineNo' => entry['lineno'],
'context' => build_stacktrace_context(entry),
'filename' => entry['filename'],
'function' => entry['function'],
'colNo' => 0 # we don't support colNo yet.
}
end
end
def find_stacktrace_from_payload
exception_entry = payload.dig('exception')
if exception_entry
exception_values = exception_entry.dig('values')
stack_trace_entry = exception_values&.detect { |h| h['stacktrace'].present? }
stack_trace_entry&.dig('stacktrace', 'frames')
end
end
def build_stacktrace_context(entry)
context = []
error_line = entry['context_line']
error_line_no = entry['lineno']
pre_context = entry['pre_context']
post_context = entry['post_context']
context += lines_with_position(pre_context, error_line_no - pre_context.size) if pre_context
context += lines_with_position([error_line], error_line_no)
context += lines_with_position(post_context, error_line_no + 1) if post_context
context.reject(&:blank?)
end
def lines_with_position(lines, position)
return [] if lines.blank?
lines.map.with_index do |line, index|
next unless line
[position + index, line]
end
end
end

View File

@ -1,30 +0,0 @@
#modal-upload-blob.modal
.modal-dialog.modal-lg
.modal-content
.modal-header
%h1.page-title.gl-font-size-h-display= title
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
%span{ "aria-hidden": "true" } &times;
.modal-body
= form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form', data: { method: method } do
.dropzone
.dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light
- upload_link = link_to s_('UploadLink|click to upload'), '#', class: "markdown-selector"
- dropzone_text = _('Attach a file by drag &amp; drop or %{upload_link}') % { upload_link: upload_link }
#{ dropzone_text.html_safe }
%br
= render Pajamas::AlertComponent.new(variant: :danger,
alert_options: { class: 'dropzone-alerts gl-alert gl-alert-danger gl-mb-5 data gl-display-none' },
dismissible: false)
= render 'shared/new_commit_form', placeholder: placeholder, ref: local_assigns[:ref]
.form-actions
= button_tag class: 'btn gl-button btn-confirm btn-upload-file gl-mr-2', id: 'submit-all', type: 'button' do
= gl_loading_icon(inline: true, css_class: 'gl-mr-2 js-loading-icon hidden')
= button_title
= link_to _("Cancel"), '#', class: "btn gl-button btn-default btn-cancel", "data-dismiss" => "modal"
= render 'shared/projects/edit_information'

View File

@ -14,8 +14,5 @@
- if can_modify_blob?(@blob)
= render 'projects/blob/remove'
- title = _("Replace %{blob_name}") % { blob_name: @blob.name }
= render 'projects/blob/upload', title: title, placeholder: title, button_title: _('Replace file'), form_path: project_update_blob_path(@project, @id), method: :put
= render partial: 'pipeline_tour_success' if show_suggest_pipeline_creation_celebration?
= render 'shared/web_ide_path'

View File

@ -76,6 +76,3 @@
%span><
git push -u origin --all
git push -u origin --tags
- if @project.upload_anchor_data.present?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, default_branch_name), ref: default_branch_name, method: :post

View File

@ -28,7 +28,7 @@
= render 'ci/variables/variable_row', form_field: 'schedule', variable: variable
= render 'ci/variables/variable_row', form_field: 'schedule'
- if @schedule.variables.size > 0
%button.gl-button.btn.btn-confirm-secondary.gl-mt-3.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: "#{@schedule.variables.size == 0}" } }
= render Pajamas::ButtonComponent.new(category: :secondary, variant: :confirm, button_options: { class: 'gl-mt-3 js-secret-value-reveal-button', data: { secret_reveal_status: "#{@schedule.variables.size == 0}" }}) do
- if @schedule.variables.size == 0
= n_('Hide value', 'Hide values', @schedule.variables.size)
- else

View File

@ -12,11 +12,12 @@
- if can?(current_user, :admin_label, @project)
%li.gl-display-inline-block.js-toggle-priority.gl-ml-3{ data: { url: remove_priority_project_label_path(@project, label),
dom_id: dom_id(label), type: label.type } }
%button.add-priority.btn.gl-button.btn-default-tertiary.btn-sm.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Prioritize label') }
= sprite_icon('star-o')
= render Pajamas::ButtonComponent.new(category: :tertiary,
icon: 'star',
button_options: { class: 'remove-priority has-tooltip', 'title': _('Remove priority'), 'aria_label': _('Deprioritize label'), data: { placement: 'bottom' } })
= render Pajamas::ButtonComponent.new(category: :tertiary,
icon: 'star-o',
button_options: { class: 'add-priority has-tooltip', title: _('Prioritize'), aria_label: _('Prioritize label'), data: { placement: 'bottom' } })
- if can?(current_user, :admin_label, label)
%li.gl-display-inline-block
= render Pajamas::ButtonComponent.new(href: label.edit_path, category: :tertiary, icon: 'pencil', button_options: { class: 'edit has-tooltip', 'title': _('Edit'), 'aria_label': _('Edit'), data: { placement: 'bottom' } })

View File

@ -1,11 +1,8 @@
.user-callout.promotion-callout.js-service-desk-callout#promote_service_desk{ data: { uid: 'promote_service_desk_dismissed' } }
.bordered-box.content-block
%button.gl-button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss Service Desk promotion' }
= sprite_icon('close', size: 16, css_class: 'dismiss-icon')
.svg-container
= custom_icon('icon_service_desk')
.user-callout-copy
%h4
= _("Improve customer support with Service Desk")
%p
= _("Service Desk allows people to create issues in your GitLab instance without their own user account. It provides a unique email address for end users to create issues in a project. Replies can be sent either through the GitLab interface or by email. End users only see threads through email.")
= render Pajamas::BannerComponent.new(banner_options: {class: 'js-service-desk-callout', data: {uid: 'promote_service_desk_dismissed'}, id: 'promote_service_desk'},
close_options: {'aria-label' => s_('Promotions|Dismiss Service Desk promotion'), class: 'js-close-callout'},
svg_path: 'illustrations/service_desk_callout.svg',
button_text: s_('Promotions|Configure Service Desk'), button_link: help_page_path('user/project/service_desk.html', anchor: 'configuring-service-desk')) do |c|
- c.title do
= _('Improve customer support with Service Desk')
%p
= _('Service Desk allows people to create issues in your GitLab instance without their own user account. It provides a unique email address for end users to create issues in a project. Replies can be sent either through the GitLab interface or by email. End users only see threads through email.')

View File

@ -1,8 +1,8 @@
---
name: bootstrap_confirmation_modals
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73167
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344658
milestone: '14.5'
name: active_support_hash_digest_sha256
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90098
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365314
milestone: '15.1'
type: development
group: group::foundations
group: group::sharding
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364112
milestone: '15.1'
type: development
group: group::integrations
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: update_vuln_identifiers_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82538
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362179
milestone: '15.1'
type: development
group: group::static analysis
default_enabled: false

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
Rails.application.configure do
# We set ActiveSupport::Digest.hash_digest_class directly copying
# See https://github.com/rails/rails/blob/6-1-stable/activesupport/lib/active_support/railtie.rb#L96-L98
#
# Note that is the only usage of config.active_support.hash_digest_class
config.after_initialize do
ActiveSupport::Digest.hash_digest_class = Gitlab::HashDigest::Facade
end
end

View File

@ -35,6 +35,8 @@ Gitlab::Seeder.quiet do
visibility_level: Gitlab::VisibilityLevel.values.sample,
content: 'foo'
}).tap do |snippet|
snippet.repository.expire_exists_cache
unless snippet.repository_exists?
Gitlab::Seeder::SnippetRepository.new(snippet).import
end
@ -48,4 +50,3 @@ Gitlab::Seeder.quiet do
Gitlab::Seeder::SnippetRepository.cleanup
end

View File

@ -117,6 +117,12 @@ From there, you can see the following actions:
- Failed attempt to create a group deploy token. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353452) in GitLab 14.9.
- [IP restrictions](../user/group/index.md#group-access-restriction-by-ip-address) changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358986) in GitLab 15.0.
- Changes to push rules. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227629) in GitLab 15.0.
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356152) in GitLab 15.1, changes to the following merge request approvals settings:
- Prevent approval by author.
- Prevent approvals by users who add commits.
- Prevent editing approval rules in projects and merge requests.
- Require user password to approve.
- Remove all approvals when commits are added to the source branch.
Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)

View File

@ -277,6 +277,12 @@ In the event of an emergency, or false positive from this job, add the
`pipeline:skip-undercoverage` label to the merge request to allow this job to
fail.
## Ruby versions testing
Our test suite runs against Ruby 2 in merge requests and default branch pipelines.
We do run our test suite against Ruby 3 on 2-hourly scheduled pipelines, as GitLab.com will soon run on Ruby 3.
## PostgreSQL versions testing
Our test suite runs against PG12 as GitLab.com runs on PG12 and

View File

@ -37,7 +37,7 @@ module API
latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
authorize_read_job_artifacts!(latest_build)
present_carrierwave_file!(latest_build.artifacts_file)
present_artifacts_file!(latest_build.artifacts_file)
end
desc 'Download a specific file from artifacts archive from a ref' do
@ -78,7 +78,7 @@ module API
build = find_build!(params[:job_id])
authorize_read_job_artifacts!(build)
present_carrierwave_file!(build.artifacts_file)
present_artifacts_file!(build.artifacts_file)
end
desc 'Download a specific file from artifacts archive' do

View File

@ -330,7 +330,7 @@ module API
authenticate_job!(require_running: false)
end
present_carrierwave_file!(current_job.artifacts_file, supports_direct_download: params[:direct_download])
present_artifacts_file!(current_job.artifacts_file, supports_direct_download: params[:direct_download])
end
end
end

View File

@ -570,11 +570,19 @@ module API
end
end
def log_artifact_size(file)
Gitlab::ApplicationContext.push(artifact: file.model)
end
def present_artifacts_file!(file, **args)
log_artifact_size(file) if file
present_carrierwave_file!(file, **args)
end
def present_carrierwave_file!(file, supports_direct_download: true)
return not_found! unless file&.exists?
log_artifact_size(file) if file.is_a?(JobArtifactUploader)
if file.file_storage?
present_disk_file!(file.path, file.filename)
elsif supports_direct_download && file.class.direct_download_enabled?
@ -725,7 +733,6 @@ module API
# Deprecated. Use `send_artifacts_entry` instead.
def legacy_send_artifacts_entry(file, entry)
header(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
log_artifact_size(file)
body ''
end
@ -733,15 +740,10 @@ module API
def send_artifacts_entry(file, entry)
header(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
header(*Gitlab::Workhorse.detect_content_type)
log_artifact_size(file)
body ''
end
def log_artifact_size(file)
Gitlab::ApplicationContext.push(artifact: file.model)
end
# The Grape Error Middleware only has access to `env` but not `params` nor
# `request`. We workaround this by defining methods that returns the right
# values.

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
module ErrorTracking
class StacktraceBuilder
attr_reader :stacktrace
def initialize(payload)
@stacktrace = build_stacktrace(payload)
end
private
def build_stacktrace(payload)
raw_stacktrace = raw_stacktrace_from_payload(payload)
return [] unless raw_stacktrace
raw_stacktrace.map do |entry|
{
'lineNo' => entry['lineno'],
'context' => build_stacktrace_context(entry),
'filename' => entry['filename'],
'function' => entry['function'],
'colNo' => 0 # we don't support colNo yet.
}
end
end
def raw_stacktrace_from_payload(payload)
exception_entry = payload['exception']
return unless exception_entry
exception_values = exception_entry['values']
stack_trace_entry = exception_values&.detect { |h| h['stacktrace'].present? }
stack_trace_entry&.dig('stacktrace', 'frames')
end
def build_stacktrace_context(entry)
error_line = entry['context_line']
error_line_no = entry['lineno']
pre_context = entry['pre_context']
post_context = entry['post_context']
context = []
context.concat lines_with_position(pre_context, error_line_no - pre_context.size) if pre_context
context.concat lines_with_position([error_line], error_line_no)
context.concat lines_with_position(post_context, error_line_no + 1) if post_context
context.reject(&:blank?)
end
def lines_with_position(lines, position)
return [] if lines.blank?
lines.map.with_index do |line, index|
next unless line
[position + index, line]
end
end
end
end

View File

@ -4,7 +4,7 @@
# they are able to only include the jobs that they find interesting.
#
# Therefore, this template is not supposed to run any jobs. The idea is to only
# create hidden jobs. See: https://docs.gitlab.com/ee/ci/yaml/#hide-jobs
# create hidden jobs. See: https://docs.gitlab.com/ee/ci/jobs/#hide-jobs
#
# There is a more opinionated template which we suggest the users to abide,
# which is the lib/gitlab/ci/templates/Terraform.gitlab-ci.yml

View File

@ -50,12 +50,14 @@ module Gitlab
end
def highlighted_diff_lines
@highlighted_diff_lines ||= begin
removal_line_maps, addition_line_maps = map_diff_block_to_source_line(
source_diff.highlighted_diff_lines, source_diff.new_file?, source_diff.deleted_file?)
Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight.map do |line|
mutate_line(line, addition_line_maps, removal_line_maps)
end
strong_memoize(:highlighted_diff_lines) do
lines = Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight
lines_in_source = lines_in_source_diff(
source_diff.highlighted_diff_lines, source_diff.deleted_file?, source_diff.new_file?
)
lines.zip(line_positions_at_source_diff(lines, transformed_blocks))
.map { |line, positions| mutate_line(line, positions, lines_in_source)}
end
end
@ -91,6 +93,10 @@ module Gitlab
diff
end
def transformed_blocks
{ from: notebook_diff.from.blocks, to: notebook_diff.to.blocks }
end
def rendered_timeout
@rendered_timeout ||= Gitlab::Metrics.counter(
:ipynb_semantic_diff_timeouts_total,
@ -108,21 +114,13 @@ module Gitlab
nil
end
def compute_line_numbers(transformed_old_pos, transformed_new_pos, addition_line_maps, removal_line_maps)
new_pos = map_transformed_line_to_source(transformed_new_pos, notebook_diff.to.blocks)
old_pos = map_transformed_line_to_source(transformed_old_pos, notebook_diff.from.blocks)
old_pos = addition_line_maps[new_pos] if old_pos == 0 && new_pos != 0
new_pos = removal_line_maps[old_pos] if new_pos == 0 && old_pos != 0
[old_pos, new_pos]
end
def mutate_line(line, addition_line_maps, removal_line_maps)
line.old_pos, line.new_pos = compute_line_numbers(line.old_pos, line.new_pos, addition_line_maps, removal_line_maps)
def mutate_line(line, mapped_positions, source_diff_lines)
line.old_pos, line.new_pos = mapped_positions
# Lines that do not appear on the original diff should not be commentable
line.type = "#{line.type || 'unchanged'}-nomappinginraw" unless addition_line_maps[line.new_pos] || removal_line_maps[line.old_pos]
unless source_diff_lines[:to].include?(line.new_pos) || source_diff_lines[:from].include?(line.old_pos)
line.type = "#{line.type || 'unchanged'}-nomappinginraw"
end
line.line_code = line_code(line)

View File

@ -4,75 +4,94 @@ module Gitlab
module Rendered
module Notebook
module DiffFileHelper
require 'set'
EMBEDDED_IMAGE_PATTERN = ' ![](data:image'
def strip_diff_frontmatter(diff_content)
diff_content.scan(/.*\n/)[2..]&.join('') if diff_content.present?
end
def map_transformed_line_to_source(transformed_line, transformed_blocks)
transformed_blocks.empty? ? 0 : ( transformed_blocks[transformed_line - 1][:source_line] || -1 ) + 1
# line_positions_at_source_diff: given the transformed lines,
# what are the correct values for old_pos and new_pos?
#
# Example:
#
# Original
# from | to
# A | A
# B | D
# C | E
# F | F
#
# Original Diff
# A A
# - B
# - C
# + D
# + E
# F F
#
# Transformed
# from | to
# A | A
# C | D
# B | J
# L | E
# K | K
# F | F
#
# Transformed diff | transf old, new | OG old_pos, new_pos |
# A A | 1, 1 | 1, 1 |
# -C | 2, 2 | 3, 2 |
# -B | 3, 2 | 2, 2 |
# -L | 4, 2 | 0, 0 |
# + D | 5, 2 | 4, 2 |
# + J | 5, 3 | 0, 0 |
# + E | 5, 4 | 4, 3 |
# K K | 5, 5 | 0, 0 |
# F F | 6, 6 | 4, 4 |
def line_positions_at_source_diff(lines, blocks)
last_mapped_old_pos = 0
last_mapped_new_pos = 0
lines.reverse_each.map do |line|
old_pos = source_line_from_block(line.old_pos, blocks[:from])
new_pos = source_line_from_block(line.new_pos, blocks[:to])
old_has_no_mapping = old_pos == 0
new_has_no_mapping = new_pos == 0
next [0, 0] if old_has_no_mapping && (new_has_no_mapping || line.type == 'old')
next [0, 0] if new_has_no_mapping && line.type == 'new'
new_pos = last_mapped_new_pos if new_has_no_mapping && line.type == 'old'
old_pos = last_mapped_old_pos if old_has_no_mapping && line.type == 'new'
last_mapped_old_pos = old_pos
last_mapped_new_pos = new_pos
[old_pos, new_pos]
end.reverse
end
# line_codes are used for assigning notes to diffs, and these depend on the line on the new version and the
# line that would have been that one in the previous version. However, since we do a transformation on the
# file, that mapping gets lost. To overcome this, we look at the original source lines and build two maps:
# - For additions, we look at the latest line change for that line and pick the old line for that id
# - For removals, we look at the first line in the old version, and pick the first line on the new version
#
# Note: ipynb files never change the first or last line (open and closure of the
# json object), unless the file is removed or deleted
#
# Example: Additions and removals
# Old: New:
# A A
# B D
# C E
# F F
#
# Diff:
# 1 A A 1 | line code: 1_1
# 2 -B | line code: 2_2 -> new line is what it is after been without the removal, 2
# 3 -C | line code: 3_2
# + D 2 | line code: 4_2 -> old line is what would have been before the addition, 4
# + E 3 | line code: 4_3
# 4 F F 4 | line code: 4_4
#
# Example: only additions
# Old: New:
# A A
# F B
# C
# F
#
# Diff:
# A A | line code: 1_1
# + B | line code: 2_2 -> old line is the next after the additions, 2
# + C | line code: 2_3
# F F | line code: 2_4
#
# Example: only removals
# Old: New:
# A A
# B F
# C
# F
#
# Diff:
# A A | line code: 1_1
# -B | line code: 2_2 -> new line is what it is after been without the removal, 2
# -C | line code: 3_2
# F F | line code: 4_2
def map_diff_block_to_source_line(lines, file_added, file_deleted)
removals = {}
additions = {}
def lines_in_source_diff(source_diff_lines, is_deleted_file, is_added_file)
{
from: is_added_file ? Set[] : source_diff_lines.map {|l| l.old_pos}.to_set,
to: is_deleted_file ? Set[] : source_diff_lines.map {|l| l.new_pos}.to_set
}
end
lines.each do |line|
removals[line.old_pos] = line.new_pos unless file_added
additions[line.new_pos] = line.old_pos unless file_deleted
end
def source_line_from_block(transformed_line, transformed_blocks)
# Blocks are the lines returned from the library and are a hash with {text:, source_line:}
# Blocks source_line are 0 indexed
return 0 if transformed_blocks.empty?
[removals, additions]
line_in_source = transformed_blocks[transformed_line - 1][:source_line]
return 0 unless line_in_source.present?
line_in_source + 1
end
def image_as_rich_text(line_text)

View File

@ -54,7 +54,6 @@ module Gitlab
push_frontend_feature_flag(:usage_data_api, type: :ops)
push_frontend_feature_flag(:security_auto_fix)
push_frontend_feature_flag(:new_header_search)
push_frontend_feature_flag(:bootstrap_confirmation_modals)
push_frontend_feature_flag(:source_editor_toolbar)
push_frontend_feature_flag(:gl_avatar_for_all_user_avatars)
push_frontend_feature_flag(:mr_attention_requests, current_user)

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Gitlab
module HashDigest
# Used for rolling out to use OpenSSL::Digest::SHA256
# for ActiveSupport::Digest
class Facade
class << self
def hexdigest(...)
hash_digest_class.hexdigest(...)
end
def hash_digest_class
if use_sha256?
::OpenSSL::Digest::SHA256
else
::Digest::MD5 # rubocop:disable Fips/MD5
end
end
def use_sha256?
return false unless Feature.feature_flags_available?
Feature.enabled?(:active_support_hash_digest_sha256)
end
end
end
end
end

View File

@ -2154,6 +2154,9 @@ msgstr ""
msgid "Add approvers"
msgstr ""
msgid "Add assignees"
msgstr ""
msgid "Add attention request"
msgstr ""
@ -5159,9 +5162,6 @@ msgstr ""
msgid "Attach a file"
msgstr ""
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
msgid "Attaching File - %{progress}"
msgstr ""
@ -30620,6 +30620,9 @@ msgstr ""
msgid "Promotions|Buy GitLab Enterprise Edition"
msgstr ""
msgid "Promotions|Configure Service Desk"
msgstr ""
msgid "Promotions|Contact an owner of group %{namespace_name} to upgrade the plan."
msgstr ""
@ -30632,6 +30635,9 @@ msgstr ""
msgid "Promotions|Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
msgid "Promotions|Dismiss Service Desk promotion"
msgstr ""
msgid "Promotions|Dismiss burndown charts promotion"
msgstr ""
@ -31908,9 +31914,6 @@ msgstr ""
msgid "Replace"
msgstr ""
msgid "Replace %{blob_name}"
msgstr ""
msgid "Replace %{name}"
msgstr ""
@ -40852,9 +40855,6 @@ msgstr ""
msgid "Upload object map"
msgstr ""
msgid "UploadLink|click to upload"
msgstr ""
msgid "Uploaded"
msgstr ""

View File

@ -3,8 +3,9 @@
require 'spec_helper'
RSpec.describe 'Admin disables 2FA for a user' do
include Spec::Support::Helpers::ModalHelpers
it 'successfully', :js do
stub_feature_flags(bootstrap_confirmation_modals: false)
admin = create(:admin)
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
@ -12,9 +13,11 @@ RSpec.describe 'Admin disables 2FA for a user' do
edit_user(user)
page.within('.two-factor-status') do
accept_confirm { click_link 'Disable' }
click_link 'Disable'
end
accept_gl_confirm(button_text: 'Disable')
page.within('.two-factor-status') do
expect(page).to have_content 'Disabled'
expect(page).not_to have_button 'Disable'

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Admin::Hooks' do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:admin) }
before do
@ -79,7 +81,6 @@ RSpec.describe 'Admin::Hooks' do
let(:hook_url) { generate(:url) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
create(:system_hook, url: hook_url)
end
@ -87,7 +88,7 @@ RSpec.describe 'Admin::Hooks' do
it 'from hooks list page' do
visit admin_hooks_path
accept_confirm { click_link 'Delete' }
accept_gl_confirm(button_text: 'Delete webhook') { click_link 'Delete' }
expect(page).not_to have_content(hook_url)
end
@ -95,7 +96,7 @@ RSpec.describe 'Admin::Hooks' do
visit admin_hooks_path
click_link 'Edit'
accept_confirm { click_link 'Delete' }
accept_gl_confirm(button_text: 'Delete webhook') { click_link 'Delete' }
expect(page).not_to have_content(hook_url)
end
end

View File

@ -16,7 +16,6 @@ RSpec.describe 'admin issues labels' do
describe 'list' do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
visit admin_labels_path
end
@ -38,11 +37,9 @@ RSpec.describe 'admin issues labels' do
end
it 'deletes all labels', :js do
page.within '.labels' do
page.all('.js-remove-label').each do |remove|
accept_confirm { remove.click }
wait_for_requests
end
page.all('.labels .js-remove-label').each do |remove|
accept_gl_confirm(button_text: 'Delete label') { remove.click }
wait_for_requests
end
wait_for_requests

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
include Spec::Support::Helpers::ModalHelpers
let(:admin) { create(:admin) }
let!(:user) { create(:user) }
@ -74,10 +76,9 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
let!(:impersonation_token) { create(:personal_access_token, :impersonation, user: user) }
it "allows revocation of an active impersonation token" do
stub_feature_flags(bootstrap_confirmation_modals: false)
visit admin_user_impersonation_tokens_path(user_id: user.username)
accept_confirm { click_on "Revoke" }
accept_gl_confirm(button_text: 'Revoke') { click_on "Revoke" }
expect(page).to have_selector(".settings-message")
expect(no_personal_access_tokens_message).to have_text("This user has no active impersonation tokens.")

View File

@ -4,11 +4,11 @@ require 'spec_helper'
RSpec.describe 'Admin uses repository checks', :request_store do
include StubENV
include Spec::Support::Helpers::ModalHelpers
let(:admin) { create(:admin) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
end
@ -57,7 +57,9 @@ RSpec.describe 'Admin uses repository checks', :request_store do
expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
accept_confirm { find(:link, 'Clear all repository checks').send_keys(:return) }
accept_gl_confirm(button_text: 'Clear repository checks') do
find(:link, 'Clear all repository checks').send_keys(:return)
end
expect(page).to have_content('Started asynchronous removal of all repository check states.')
end

View File

@ -10,7 +10,6 @@ RSpec.describe 'Admin::Users::User' do
let_it_be(:current_user) { create(:admin) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
end
@ -354,7 +353,7 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_content("Secondary email: #{secondary_email.email}")
accept_confirm { find("#remove_email_#{secondary_email.id}").click }
accept_gl_confirm { find("#remove_email_#{secondary_email.id}").click }
expect(page).not_to have_content(secondary_email.email)
end

View File

@ -10,7 +10,6 @@ RSpec.describe 'Admin::Users' do
let_it_be(:current_user) { create(:admin) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
end
@ -504,8 +503,11 @@ RSpec.describe 'Admin::Users' do
it 'allows group membership to be revoked', :js do
page.within(first('.group_member')) do
accept_confirm { find('.btn[data-testid="remove-user"]').click }
find('.btn[data-testid="remove-user"]').click
end
accept_gl_confirm(button_text: 'Remove')
wait_for_requests
expect(page).not_to have_selector('.group_member')

View File

@ -521,7 +521,6 @@ RSpec.describe 'Project issue boards', :js do
let_it_be(:user_guest) { create(:user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_guest(user_guest)
sign_in(user_guest)
visit project_board_path(project, board)

View File

@ -4,13 +4,13 @@ require 'spec_helper'
RSpec.describe 'Groups > Members > Leave group' do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:other_user) { create(:user) }
let(:group) { create(:group) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end
@ -32,7 +32,7 @@ RSpec.describe 'Groups > Members > Leave group' do
visit group_path(group, leave: 1)
page.accept_confirm
accept_gl_confirm(button_text: 'Leave group')
wait_for_all_requests
expect(page).to have_current_path(dashboard_groups_path, ignore_query: true)

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Group > Settings > Access Tokens', :js do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
let_it_be(:bot_user) { create(:user, :project_bot) }
let_it_be(:group) { create(:group) }
@ -13,7 +15,6 @@ RSpec.describe 'Group > Settings > Access Tokens', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end

View File

@ -14,7 +14,6 @@ RSpec.describe 'User comments on a diff', :js do
let(:user) { create(:user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -19,7 +19,6 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
project.add_developer(user)
sign_in(user)
stub_feature_flags(bootstrap_confirmation_modals: false)
stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 104)
end

View File

@ -18,7 +18,6 @@ RSpec.describe 'Merge request > User posts notes', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -83,7 +83,6 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
%w(parallel).each do |view|
context "#{view} view" do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
visit diffs_project_merge_request_path(project, merge_request, view: view)
wait_for_requests

View File

@ -115,7 +115,6 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
build.success!
deployment.update!(on_stop: manual.name)
visit project_merge_request_path(project, merge_request)

View File

@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe 'Profile account page', :js do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end
@ -65,11 +66,17 @@ RSpec.describe 'Profile account page', :js do
it 'allows resetting of feed token' do
visit profile_personal_access_tokens_path
previous_token = ''
within('[data-testid="feed-token-container"]') do
previous_token = find_field('Feed token').value
accept_confirm { click_link('reset this token') }
click_link('reset this token')
end
accept_gl_confirm
within('[data-testid="feed-token-container"]') do
click_button('Click to reveal')
expect(find_field('Feed token').value).not_to eq(previous_token)
@ -81,11 +88,17 @@ RSpec.describe 'Profile account page', :js do
visit profile_personal_access_tokens_path
previous_token = ''
within('[data-testid="incoming-email-token-container"]') do
previous_token = find_field('Incoming email token').value
accept_confirm { click_link('reset this token') }
click_link('reset this token')
end
accept_gl_confirm
within('[data-testid="incoming-email-token-container"]') do
click_button('Click to reveal')
expect(find_field('Incoming email token').value).not_to eq(previous_token)

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
include Spec::Support::Helpers::ModalHelpers
let(:user) do
create(:user).tap do |user|
user.current_sign_in_at = Time.current
@ -11,10 +13,6 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
let(:admin) { create(:admin) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
end
it 'user sees their active sessions' do
travel_to(Time.zone.parse('2018-03-12 09:06')) do
Capybara::Session.new(:session1)
@ -101,7 +99,9 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
expect(page).to have_link('Revoke', count: 1)
accept_confirm { click_on 'Revoke' }
accept_gl_confirm(button_text: 'Revoke') do
click_on 'Revoke'
end
expect(page).not_to have_link('Revoke')
end

View File

@ -3,11 +3,12 @@
require 'spec_helper'
RSpec.describe 'Profile > Applications' do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:application) { create(:oauth_application, owner: user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end
@ -25,9 +26,11 @@ RSpec.describe 'Profile > Applications' do
page.within('.oauth-applications') do
expect(page).to have_content('Your applications (1)')
accept_confirm { click_button 'Destroy' }
click_button 'Destroy'
end
accept_gl_confirm(button_text: 'Destroy')
expect(page).to have_content('The application was deleted successfully')
expect(page).to have_content('Your applications (0)')
expect(page).to have_content('Authorized applications (0)')
@ -39,9 +42,11 @@ RSpec.describe 'Profile > Applications' do
page.within('.oauth-authorized-applications') do
expect(page).to have_content('Authorized applications (1)')
accept_confirm { click_button 'Revoke' }
click_button 'Revoke'
end
accept_gl_confirm(button_text: 'Revoke application')
expect(page).to have_content('The application was revoked access.')
expect(page).to have_content('Your applications (0)')
expect(page).to have_content('Authorized applications (0)')

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Profile > Personal Access Tokens', :js do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:pat_create_service) { double('PersonalAccessTokens::CreateService', execute: ServiceResponse.error(message: 'error', payload: { personal_access_token: PersonalAccessToken.new })) }
@ -19,7 +21,6 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end
@ -94,7 +95,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
it "allows revocation of an active token" do
visit profile_personal_access_tokens_path
accept_confirm { click_on "Revoke" }
accept_gl_confirm(button_text: 'Revoke') { click_on "Revoke" }
expect(active_personal_access_tokens).to have_text("This user has no active personal access tokens.")
end
@ -113,7 +114,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
end
visit profile_personal_access_tokens_path
accept_confirm { click_on "Revoke" }
accept_gl_confirm(button_text: "Revoke") { click_on "Revoke" }
expect(active_personal_access_tokens).to have_text(personal_access_token.name)
end
end

View File

@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe "User deletes branch", :js do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
let(:project) { create(:project, :repository) }
@ -24,9 +26,7 @@ RSpec.describe "User deletes branch", :js do
find('.js-delete-branch-button').click
end
page.within '.modal-footer' do
click_button 'Yes, delete branch'
end
accept_gl_confirm(button_text: 'Yes, delete branch')
wait_for_requests

View File

@ -4,6 +4,7 @@ require "spec_helper"
RSpec.describe "User deletes comments on a commit", :js do
include Spec::Support::Helpers::Features::NotesHelpers
include Spec::Support::Helpers::ModalHelpers
include RepoHelpers
let(:comment_text) { "XML attached" }
@ -11,7 +12,6 @@ RSpec.describe "User deletes comments on a commit", :js do
let(:user) { create(:user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
project.add_developer(user)
@ -32,9 +32,11 @@ RSpec.describe "User deletes comments on a commit", :js do
find(".more-actions").click
find(".more-actions .dropdown-menu li", match: :first)
accept_confirm { find(".js-note-delete").click }
find(".js-note-delete").click
end
accept_gl_confirm(button_text: 'Delete comment')
expect(page).not_to have_css(".note")
end
end

View File

@ -4,6 +4,7 @@ require "spec_helper"
RSpec.describe "User comments on commit", :js do
include Spec::Support::Helpers::Features::NotesHelpers
include Spec::Support::Helpers::ModalHelpers
include RepoHelpers
let_it_be(:project) { create(:project, :repository) }
@ -93,8 +94,6 @@ RSpec.describe "User comments on commit", :js do
context "when deleting comment" do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
visit(project_commit_path(project, sample_commit.id))
add_note(comment_text)
@ -112,9 +111,11 @@ RSpec.describe "User comments on commit", :js do
find(".more-actions").click
find(".more-actions .dropdown-menu li", match: :first)
accept_confirm { find(".js-note-delete").click }
find(".js-note-delete").click
end
accept_gl_confirm(button_text: 'Delete comment')
expect(page).not_to have_css(".note")
end
end

View File

@ -132,8 +132,6 @@ RSpec.describe 'Environments page', :js do
create(:environment, project: project, state: :available)
end
stub_feature_flags(bootstrap_confirmation_modals: false)
context 'when there are no deployments' do
before do
visit_environments(project)

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User browses a job', :js do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:user_access_level) { :developer }
let(:project) { create(:project, :repository, namespace: user.namespace) }
@ -12,7 +14,6 @@ RSpec.describe 'User browses a job', :js do
before do
project.add_maintainer(user)
project.enable_ci
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end
@ -26,7 +27,11 @@ RSpec.describe 'User browses a job', :js do
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
accept_confirm { find('[data-testid="job-log-erase-link"]').click }
accept_gl_confirm(button_text: 'Erase job log') do
find('[data-testid="job-log-erase-link"]').click
end
wait_for_requests
expect(page).to have_no_css('.artifacts')
expect(build).not_to have_trace

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Projects > Members > Member leaves project' do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :repository, :with_namespace_settings) }
@ -11,7 +12,6 @@ RSpec.describe 'Projects > Members > Member leaves project' do
before do
project.add_developer(user)
sign_in(user)
stub_feature_flags(bootstrap_confirmation_modals: false)
end
it 'user leaves project' do
@ -26,7 +26,7 @@ RSpec.describe 'Projects > Members > Member leaves project' do
it 'user leaves project by url param', :js do
visit project_path(project, leave: 1)
page.accept_confirm
accept_gl_confirm(button_text: 'Leave project')
wait_for_all_requests
expect(page).to have_current_path(dashboard_projects_path, ignore_query: true)

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Members > User requests access', :js do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
@ -13,7 +15,6 @@ RSpec.describe 'Projects > Members > User requests access', :js do
sign_in(user)
project.add_maintainer(maintainer)
visit project_path(project)
stub_feature_flags(bootstrap_confirmation_modals: false)
end
it 'request access feature is disabled' do
@ -67,7 +68,7 @@ RSpec.describe 'Projects > Members > User requests access', :js do
expect(project.requesters.exists?(user_id: user)).to be_truthy
accept_confirm { click_link 'Withdraw Access Request' }
accept_gl_confirm { click_link 'Withdraw Access Request' }
expect(page).not_to have_content 'Withdraw Access Request'
expect(page).to have_content 'Request Access'

View File

@ -3,6 +3,7 @@ require 'spec_helper'
RSpec.describe 'User adds pages domain', :js do
include LetsEncryptHelpers
include Spec::Support::Helpers::ModalHelpers
let_it_be(:project) { create(:project, pages_https_only: false) }
@ -14,8 +15,6 @@ RSpec.describe 'User adds pages domain', :js do
project.add_maintainer(user)
sign_in(user)
stub_feature_flags(bootstrap_confirmation_modals: false)
end
context 'when pages are exposed on external HTTP address', :http_pages_enabled do
@ -168,7 +167,7 @@ RSpec.describe 'User adds pages domain', :js do
within('#content-body') { click_link 'Edit' }
accept_confirm { click_link 'Remove' }
accept_gl_confirm(button_text: 'Remove certificate') { click_link 'Remove' }
expect(page).to have_field('Certificate (PEM)', with: '')
expect(page).to have_field('Key (PEM)', with: '')

View File

@ -3,6 +3,7 @@ require 'spec_helper'
RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
include LetsEncryptHelpers
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project, pages_https_only: false) }
let(:user) { create(:user) }
@ -14,7 +15,6 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
stub_lets_encrypt_settings
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_role(user, role)
sign_in(user)
@ -139,7 +139,8 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
expect(page).to have_selector '.card-header', text: 'Certificate'
expect(page).to have_text domain.subject
within('.card') { accept_confirm { click_on 'Remove' } }
within('.card') { click_on 'Remove' }
accept_gl_confirm(button_text: 'Remove certificate')
expect(page).to have_field 'Certificate (PEM)', with: ''
expect(page).to have_field 'Key (PEM)', with: ''
end

View File

@ -2,6 +2,8 @@
require 'spec_helper'
RSpec.describe 'Pages edits pages settings', :js do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project, pages_https_only: false) }
let(:user) { create(:user) }
@ -176,7 +178,6 @@ RSpec.describe 'Pages edits pages settings', :js do
describe 'Remove page' do
context 'when pages are deployed' do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.mark_pages_as_deployed
end
@ -185,7 +186,7 @@ RSpec.describe 'Pages edits pages settings', :js do
expect(page).to have_link('Remove pages')
accept_confirm { click_link 'Remove pages' }
accept_gl_confirm(button_text: 'Remove pages') { click_link 'Remove pages' }
expect(page).to have_content('Pages were scheduled for removal')
expect(project.reload.pages_deployed?).to be_falsey

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Pipeline Schedules', :js do
include Spec::Support::Helpers::ModalHelpers
let!(:project) { create(:project, :repository) }
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
let!(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
@ -11,7 +13,6 @@ RSpec.describe 'Pipeline Schedules', :js do
context 'logged in as the pipeline schedule owner' do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_developer(user)
pipeline_schedule.update!(owner: user)
gitlab_sign_in(user)
@ -81,7 +82,6 @@ RSpec.describe 'Pipeline Schedules', :js do
context 'logged in as a project maintainer' do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
project.add_maintainer(user)
gitlab_sign_in(user)
end
@ -117,7 +117,9 @@ RSpec.describe 'Pipeline Schedules', :js do
end
it 'deletes the pipeline' do
accept_confirm { click_link 'Delete' }
click_link 'Delete'
accept_gl_confirm(button_text: 'Delete pipeline schedule')
expect(page).not_to have_css(".pipeline-schedule-table-row")
end

View File

@ -311,7 +311,6 @@ RSpec.describe 'Pipelines', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
visit_project_pipelines
end

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Project > Settings > Access Tokens', :js do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:user) { create(:user) }
let_it_be(:bot_user) { create(:user, :project_bot) }
let_it_be(:group) { create(:group) }
@ -14,7 +16,6 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end

View File

@ -7,7 +7,6 @@ RSpec.describe 'User searches project settings', :js do
let_it_be(:project) { create(:project, :repository, namespace: user.namespace, pages_https_only: false) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
end

View File

@ -27,7 +27,7 @@ RSpec.describe 'Promotions', :js do
visit edit_project_path(project)
within('#promote_service_desk') do
find('.close').click
find('.js-close').click
end
wait_for_requests

View File

@ -1,93 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Projects > Files > User replaces files', :js do
include DropzoneHelper
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
let(:project) { create(:project, :repository, name: 'Shop') }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) }
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
let(:user) { create(:user) }
before do
stub_feature_flags(refactor_blob_viewer: false)
sign_in(user)
end
context 'when an user has write access' do
before do
project.add_maintainer(user)
visit(project_tree_path_root_ref)
wait_for_requests
end
it 'replaces an existed file with a new one' do
click_link('.gitignore')
expect(page).to have_content('.gitignore')
click_on('Replace')
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'))
page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'Replacement file commit message')
end
click_button('Replace file')
expect(page).to have_content('Lorem ipsum dolor sit amet')
expect(page).to have_content('Sed ut perspiciatis unde omnis')
expect(page).to have_content('Replacement file commit message')
end
end
context 'when an user does not have write access' do
before do
project2.add_reporter(user)
visit(project2_tree_path_root_ref)
wait_for_requests
end
it 'replaces an existed file with a new one in a forked project', :sidekiq_might_not_need_inline do
click_link('.gitignore')
expect(page).to have_content('.gitignore')
click_on('Replace')
expect(page).to have_link('Fork')
expect(page).to have_button('Cancel')
click_link('Fork')
expect(page).to have_content(fork_message)
click_on('Replace')
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'))
page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'Replacement file commit message')
end
click_button('Replace file')
expect(page).to have_content('Replacement file commit message')
fork = user.fork_of(project2.reload)
expect(page).to have_current_path(project_new_merge_request_path(fork), ignore_query: true)
click_link('Changes')
expect(page).to have_content('Lorem ipsum dolor sit amet')
expect(page).to have_content('Sed ut perspiciatis unde omnis')
end
end
end

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Comments on personal snippets', :js do
include NoteInteractionHelpers
include Spec::Support::Helpers::ModalHelpers
let_it_be(:snippet) { create(:personal_snippet, :public) }
let_it_be(:other_note) { create(:note_on_personal_snippet) }
@ -18,7 +19,6 @@ RSpec.describe 'Comments on personal snippets', :js do
end
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in user
visit snippet_path(snippet)
@ -142,9 +142,11 @@ RSpec.describe 'Comments on personal snippets', :js do
open_more_actions_dropdown(snippet_notes[0])
page.within("#notes-list li#note_#{snippet_notes[0].id}") do
accept_confirm { click_on 'Delete comment' }
click_on 'Delete comment'
end
accept_gl_confirm(button_text: 'Delete comment')
wait_for_requests
expect(page).not_to have_selector("#notes-list li#note_#{snippet_notes[0].id}")

View File

@ -16,7 +16,6 @@ RSpec.describe 'User creates snippet', :js do
let(:snippet_title_field) { 'snippet-title' }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
sign_in(user)
visit new_snippet_path

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Triggers', :js do
include Spec::Support::Helpers::ModalHelpers
let(:trigger_title) { 'trigger desc' }
let(:user) { create(:user) }
let(:user2) { create(:user) }
@ -74,7 +76,6 @@ RSpec.describe 'Triggers', :js do
describe 'trigger "Revoke" workflow' do
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
end
@ -86,7 +87,7 @@ RSpec.describe 'Triggers', :js do
it 'revoke trigger' do
# See if "Revoke" on trigger works post trigger creation
page.accept_confirm do
accept_gl_confirm(button_text: 'Revoke') do
find('[data-testid="trigger_revoke_button"]').send_keys(:return)
end

View File

@ -1,49 +0,0 @@
import $ from 'jquery';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import BlobFileDropzone from '~/blob/blob_file_dropzone';
describe('BlobFileDropzone', () => {
let dropzone;
let replaceFileButton;
beforeEach(() => {
loadHTMLFixture('blob/show.html');
const form = $('.js-upload-blob-form');
// eslint-disable-next-line no-new
new BlobFileDropzone(form, 'POST');
dropzone = $('.js-upload-blob-form .dropzone').get(0).dropzone;
dropzone.processQueue = jest.fn();
replaceFileButton = $('#submit-all');
});
afterEach(() => {
resetHTMLFixture();
});
describe('submit button', () => {
it('requires file', () => {
jest.spyOn(window, 'alert').mockImplementation(() => {});
replaceFileButton.click();
expect(window.alert).toHaveBeenCalled();
});
it('is disabled while uploading', () => {
jest.spyOn(window, 'alert').mockImplementation(() => {});
const file = new File([], 'some-file.jpg');
const fakeEvent = $.Event('drop', {
dataTransfer: { files: [file] },
});
dropzone.listeners[0].events.drop(fakeEvent);
replaceFileButton.click();
expect(window.alert).not.toHaveBeenCalled();
expect(replaceFileButton.is(':disabled')).toEqual(true);
expect(dropzone.processQueue).toHaveBeenCalled();
});
});
});

View File

@ -82,7 +82,7 @@ describe('~/environments/components/environments_folder.vue', () => {
expect(collapse.attributes('visible')).toBeUndefined();
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
expect(iconNames).toEqual(['angle-right', 'folder-o']);
expect(iconNames).toEqual(['chevron-lg-right', 'folder-o']);
expect(folderName.classes('gl-font-weight-bold')).toBe(false);
expect(link.exists()).toBe(false);
});
@ -95,7 +95,7 @@ describe('~/environments/components/environments_folder.vue', () => {
expect(button.attributes('aria-label')).toBe(__('Collapse'));
expect(collapse.attributes('visible')).toBe('visible');
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
expect(iconNames).toEqual(['angle-down', 'folder-open']);
expect(iconNames).toEqual(['chevron-lg-down', 'folder-open']);
expect(folderName.classes('gl-font-weight-bold')).toBe(true);
expect(link.attributes('href')).toBe(nestedEnvironment.latest.folderPath);

View File

@ -374,7 +374,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
it('is collapsed by default', () => {
expect(collapse.attributes('visible')).toBeUndefined();
expect(icon.props('name')).toEqual('chevron-lg-right');
expect(icon.props('name')).toBe('chevron-lg-right');
expect(environmentName.classes('gl-font-weight-bold')).toBe(false);
});
@ -385,7 +385,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
expect(button.attributes('aria-label')).toBe(__('Collapse'));
expect(collapse.attributes('visible')).toBe('visible');
expect(icon.props('name')).toEqual('chevron-lg-down');
expect(icon.props('name')).toBe('chevron-lg-down');
expect(environmentName.classes('gl-font-weight-bold')).toBe(true);
expect(findDeployment().isVisible()).toBe(true);
});

View File

@ -1,6 +1,8 @@
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import { GlLink, GlTokenSelector } from '@gitlab/ui';
import { nextTick } from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
import localUpdateWorkItemMutation from '~/work_items/graphql/local_update_work_item.mutation.graphql';
const mockAssignees = [
{
@ -21,22 +23,92 @@ const mockAssignees = [
},
];
const workItemId = 'gid://gitlab/WorkItem/1';
const mutate = jest.fn();
describe('WorkItemAssignees component', () => {
let wrapper;
const findAssigneeLinks = () => wrapper.findAllComponents(GlLink);
const findTokenSelector = () => wrapper.findComponent(GlTokenSelector);
const createComponent = () => {
wrapper = shallowMount(WorkItemAssignees, {
const findEmptyState = () => wrapper.findByTestId('empty-state');
const createComponent = ({ assignees = mockAssignees } = {}) => {
wrapper = mountExtended(WorkItemAssignees, {
propsData: {
assignees: mockAssignees,
assignees,
workItemId,
},
mocks: {
$apollo: {
mutate,
},
},
attachTo: document.body,
});
};
afterEach(() => {
wrapper.destroy();
});
it('should pass the correct data-user-id attribute', () => {
createComponent();
expect(findAssigneeLinks().at(0).attributes('data-user-id')).toBe('1');
});
describe('when there are no assignees', () => {
beforeEach(() => {
createComponent({ assignees: [] });
});
it('should render empty state placeholder', () => {
expect(findEmptyState().exists()).toBe(true);
});
it('should hide empty state placeholder on focusing token selector', async () => {
findTokenSelector().vm.$emit('focus');
await nextTick();
expect(findEmptyState().exists()).toBe(false);
});
});
describe('when there are assignees', () => {
beforeEach(() => {
createComponent();
});
it('should not render empty state placeholder', () => {
expect(findEmptyState().exists()).toBe(false);
});
it('should focus token selector on token removal', async () => {
findTokenSelector().vm.$emit('token-remove', mockAssignees[0].id);
await nextTick();
expect(findEmptyState().exists()).toBe(false);
expect(findTokenSelector().element.contains(document.activeElement)).toBe(true);
});
it('should call a mutation on clicking outside the token selector', async () => {
findTokenSelector().vm.$emit('input', [mockAssignees[0]]);
findTokenSelector().vm.$emit('token-remove');
await nextTick();
expect(mutate).not.toHaveBeenCalled();
findTokenSelector().vm.$emit('blur', new FocusEvent({ relatedTarget: null }));
await nextTick();
expect(mutate).toHaveBeenCalledWith({
mutation: localUpdateWorkItemMutation,
variables: {
input: { id: workItemId, assigneeIds: [mockAssignees[0].id] },
},
});
});
});
});

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'setting ActiveSupport::Digest.hash_digest_class' do
it 'sets overrides config.active_support.hash_digest_class' do
expect(ActiveSupport::Digest.hash_digest_class).to eq(Gitlab::HashDigest::Facade)
end
end

View File

@ -0,0 +1,95 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'support/helpers/fast_rails_root'
require 'oj'
RSpec.describe ErrorTracking::StacktraceBuilder do
include FastRailsRoot
describe '#stacktrace' do
let(:original_payload) { Gitlab::Json.parse(File.read(rails_root_join('spec/fixtures', payload_file))) }
let(:payload) { original_payload }
let(:payload_file) { 'error_tracking/parsed_event.json' }
subject(:stacktrace) { described_class.new(payload).stacktrace }
context 'with full error context' do
it 'generates a correct stacktrace in expected format' do
expected_context = [
[132, " end\n"],
[133, "\n"],
[134, " begin\n"],
[135, " block.call(work, *extra)\n"],
[136, " rescue Exception => e\n"],
[137, " STDERR.puts \"Error reached top of thread-pool: #\{e.message\} (#\{e.class\})\"\n"],
[138, " end\n"]
]
expected_entry = {
'lineNo' => 135,
'context' => expected_context,
'filename' => 'puma/thread_pool.rb',
'function' => 'block in spawn_thread',
'colNo' => 0
}
expect(stacktrace).to be_kind_of(Array)
expect(stacktrace.first).to eq(expected_entry)
end
end
context 'when error context is missing' do
let(:payload_file) { 'error_tracking/browser_event.json' }
it 'generates a stacktrace without context' do
expected_entry = {
'lineNo' => 6395,
'context' => [],
'filename' => 'webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js',
'function' => 'hydrate',
'colNo' => 0
}
expect(stacktrace).to be_kind_of(Array)
expect(stacktrace.first).to eq(expected_entry)
end
end
context 'with empty payload' do
let(:payload) { {} }
it { is_expected.to eq([]) }
end
context 'without exception field' do
let(:payload) { original_payload.except('exception') }
it { is_expected.to eq([]) }
end
context 'without exception.values field' do
before do
original_payload['exception'].delete('values')
end
it { is_expected.to eq([]) }
end
context 'without any exception.values[].stacktrace fields' do
before do
original_payload.dig('exception', 'values').each { |value| value['stacktrace'] = '' }
end
it { is_expected.to eq([]) }
end
context 'without any exception.values[].stacktrace.frame fields' do
before do
original_payload.dig('exception', 'values').each { |value| value['stacktrace'].delete('frames') }
end
it { is_expected.to eq([]) }
end
end
end

View File

@ -2,6 +2,15 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
require 'set'
MOCK_LINE = Struct.new(:text, :type, :index, :old_pos, :new_pos)
def make_lines(old_lines, new_lines, texts = nil, types = nil)
old_lines.each_with_index.map do |old, i|
MOCK_LINE.new(texts ? texts[i] : '', types ? types[i] : nil, i, old, new_lines[i])
end
end
RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
let(:dummy) { Class.new { include Gitlab::Diff::Rendered::Notebook::DiffFileHelper }.new }
@ -25,7 +34,7 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
describe '#map_transformed_line_to_source' do
using RSpec::Parameterized::TableSyntax
subject { dummy.map_transformed_line_to_source(1, transformed_blocks) }
subject { dummy.source_line_from_block(1, transformed_blocks) }
where(:case, :transformed_blocks, :result) do
'if transformed diff is empty' | [] | 0
@ -38,71 +47,6 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
end
end
describe '#map_diff_block_to_source_line' do
let(:file_added) { false }
let(:file_deleted) { false }
let(:old_positions) { [1] }
let(:new_positions) { [1] }
let(:lines) { old_positions.zip(new_positions).map { |old, new| Gitlab::Diff::Line.new("", "", 0, old, new) } }
subject { dummy.map_diff_block_to_source_line(lines, file_added, file_deleted)}
context 'only additions' do
let(:old_positions) { [1, 2, 2, 2] }
let(:new_positions) { [1, 2, 3, 4] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 4 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
end
end
context 'only additions' do
let(:old_positions) { [1, 2, 3, 4] }
let(:new_positions) { [1, 2, 2, 2] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 4 })
end
end
context 'with additions and removals' do
let(:old_positions) { [1, 2, 3, 4, 4, 4] }
let(:new_positions) { [1, 2, 2, 2, 3, 4] }
it 'computes the removals correctly' do
expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 4 })
end
it 'computes the additions correctly' do
expect(subject[1]).to eq({ 1 => 1, 2 => 4, 3 => 4, 4 => 4 })
end
end
context 'is new file' do
let(:file_added) { true }
it 'removals is empty' do
expect(subject[0]).to be_empty
end
end
context 'is deleted file' do
let(:file_deleted) { true }
it 'additions is empty' do
expect(subject[1]).to be_empty
end
end
end
describe '#image_as_rich_text' do
let(:img) { 'data:image/png;base64,some_image_here' }
let(:line_text) { " ![](#{img})"}
@ -131,4 +75,60 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
end
end
end
describe '#line_positions_at_source_diff' do
using RSpec::Parameterized::TableSyntax
let(:blocks) do
{
from: [0, 2, 1, nil, nil, 3].map { |i| { source_line: i } },
to: [0, 1, nil, 2, nil, 3].map { |i| { source_line: i } }
}
end
let(:lines) do
make_lines(
[1, 2, 3, 4, 5, 5, 5, 5, 6],
[1, 2, 2, 2, 2, 3, 4, 5, 6],
'ACBLDJEKF'.split(""),
[nil, 'old', 'old', 'old', 'new', 'new', 'new', nil, nil]
)
end
subject { dummy.line_positions_at_source_diff(lines, blocks)[index] }
where(:case, :index, :transformed_positions, :mapped_positions) do
" A A" | 0 | [1, 1] | [1, 1] # No change, old_pos and new_pos have mappings
"- C " | 1 | [2, 2] | [3, 2] # A removal, both old_pos and new_pos have valid mappings
"- B " | 2 | [3, 2] | [2, 2] # A removal, both old_pos and new_pos have valid mappings
"- L " | 3 | [4, 2] | [0, 0] # A removal, but old_pos has no mapping
"+ D" | 4 | [5, 2] | [4, 2] # An addition, new_pos has mapping but old_pos does not, so old_pos is remapped
"+ J" | 5 | [5, 3] | [0, 0] # An addition, but new_pos has no mapping, so neither are remapped
"+ E" | 6 | [5, 4] | [4, 3] # An addition, new_pos has mapping but old_pos does not, so old_pos is remapped
" K K" | 7 | [5, 5] | [0, 0] # This has no mapping
" F F" | 8 | [6, 6] | [4, 4] # No change, old_pos and new_pos have mappings
end
with_them do
it { is_expected.to eq(mapped_positions) }
end
end
describe '#lines_in_source_diff' do
using RSpec::Parameterized::TableSyntax
let(:lines) { make_lines(old_lines, new_lines) }
subject { dummy.lines_in_source_diff(lines, is_deleted, is_new) }
where(:old_lines, :new_lines, :is_deleted, :is_new, :existing_lines) do
[1, 2, 2] | [1, 1, 4] | false | false | { from: Set[1, 2], to: Set[1, 4] }
[1, 2, 2] | [1, 1, 4] | true | false | { from: Set[1, 2], to: Set[] }
[1, 2, 2] | [1, 1, 4] | false | true | { from: Set[], to: Set[1, 4] }
end
with_them do
it { is_expected.to eq(existing_lines) }
end
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::HashDigest::Facade do
describe '.hexdigest' do
let(:plaintext) { 'something that is plaintext' }
let(:sha256_hash) { OpenSSL::Digest::SHA256.hexdigest(plaintext) }
let(:md5_hash) { Digest::MD5.hexdigest(plaintext) } # rubocop:disable Fips/MD5
it 'uses SHA256' do
expect(described_class.hexdigest(plaintext)).to eq(sha256_hash)
end
context 'when feature flags is not available' do
before do
allow(Feature).to receive(:feature_flags_available?).and_return(false)
end
it 'uses MD5' do
expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
end
end
context 'when active_support_hash_digest_sha256 FF is disabled' do
before do
stub_feature_flags(active_support_hash_digest_sha256: false)
end
it 'uses MD5' do
expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
end
end
end
end

View File

@ -24,11 +24,8 @@ RSpec.describe Gitlab::UsageDataMetrics do
expect(subject).to include(:hostname)
end
it 'includes counts keys' do
it 'includes counts keys', :aggregate_failures do
expect(subject[:counts]).to include(:boards)
end
it 'includes counts keys' do
expect(subject[:counts]).to include(:issues)
end

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe ErrorTracking::ErrorEvent, type: :model do
RSpec.describe ErrorTracking::ErrorEvent do
include AfterNextHelpers
let_it_be(:event) { create(:error_tracking_error_event) }
describe 'relationships' do
@ -18,44 +20,12 @@ RSpec.describe ErrorTracking::ErrorEvent, type: :model do
end
describe '#stacktrace' do
it 'generates a correct stacktrace in expected format' do
expected_context = [
[132, " end\n"],
[133, "\n"],
[134, " begin\n"],
[135, " block.call(work, *extra)\n"],
[136, " rescue Exception => e\n"],
[137, " STDERR.puts \"Error reached top of thread-pool: #\{e.message\} (#\{e.class\})\"\n"],
[138, " end\n"]
]
expected_entry = {
'lineNo' => 135,
'context' => expected_context,
'filename' => 'puma/thread_pool.rb',
'function' => 'block in spawn_thread',
'colNo' => 0
}
it 'builds a stacktrace' do
expect_next(ErrorTracking::StacktraceBuilder, event.payload)
.to receive(:stacktrace).and_call_original
expect(event.stacktrace).to be_kind_of(Array)
expect(event.stacktrace.first).to eq(expected_entry)
end
context 'error context is missing' do
let(:event) { create(:error_tracking_error_event, :browser) }
it 'generates a stacktrace without context' do
expected_entry = {
'lineNo' => 6395,
'context' => [],
'filename' => 'webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js',
'function' => 'hydrate',
'colNo' => 0
}
expect(event.stacktrace).to be_kind_of(Array)
expect(event.stacktrace.first).to eq(expected_entry)
end
expect(event.stacktrace).not_to be_empty
end
end

View File

@ -26,6 +26,11 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
script: exit 0
needs: [a1]
a3:
stage: a
script: exit 0
needs: [a2]
b1:
stage: b
script: exit 0
@ -59,6 +64,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'created',
a3: 'created',
b1: 'pending',
b2: 'created',
c1: 'created',
@ -69,6 +75,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'created',
a3: 'created',
b1: 'success',
b2: 'created',
c1: 'created',
@ -79,6 +86,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'failed',
a2: 'skipped',
a3: 'skipped',
b1: 'success',
b2: 'skipped',
c1: 'skipped',
@ -90,6 +98,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'skipped',
a3: 'skipped',
b1: 'success',
b2: 'skipped',
c1: 'skipped',
@ -103,12 +112,42 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'created',
a3: 'skipped',
b1: 'success',
b2: 'created',
c1: 'created',
c2: 'created'
)
end
context 'when executed by a different user than the original owner' do
let(:retryer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:service) { described_class.new(project, retryer) }
it 'reassigns jobs with updated statuses to the retryer' do
expect(jobs_name_status_owner_needs).to contain_exactly(
{ 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'a2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a1'] },
{ 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
{ 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'b2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
{ 'name' => 'c1', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['b2'] },
{ 'name' => 'c2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => [] }
)
execute_after_requeue_service(a1)
expect(jobs_name_status_owner_needs).to contain_exactly(
{ 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'a2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a1'] },
{ 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
{ 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'b2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
{ 'name' => 'c1', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['b2'] },
{ 'name' => 'c2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => [] }
)
end
end
end
context 'stage-dag mixed pipeline with some same-stage needs' do
@ -212,6 +251,12 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
pipeline.processables.latest
end
def jobs_name_status_owner_needs
processables.reload.map do |job|
job.attributes.slice('name', 'status', 'user_id').merge('needs' => job.needs.map(&:name))
end
end
def execute_after_requeue_service(processable)
service.execute(processable)
end

View File

@ -2,6 +2,7 @@
RSpec.shared_examples 'hardware device for 2fa' do |device_type|
include Spec::Support::Helpers::Features::TwoFactorHelpers
include Spec::Support::Helpers::ModalHelpers
def register_device(device_type, **kwargs)
case device_type.downcase
@ -18,7 +19,6 @@ RSpec.shared_examples 'hardware device for 2fa' do |device_type|
let(:user) { create(:user) }
before do
stub_feature_flags(bootstrap_confirmation_modals: false)
gitlab_sign_in(user)
user.update_attribute(:otp_required_for_login, true)
end
@ -59,7 +59,7 @@ RSpec.shared_examples 'hardware device for 2fa' do |device_type|
expect(page).to have_content(first_device.name)
expect(page).to have_content(second_device.name)
accept_confirm { click_on 'Delete', match: :first }
accept_gl_confirm(button_text: 'Delete') { click_on 'Delete', match: :first }
expect(page).to have_content('Successfully deleted')
expect(page.body).not_to have_content(first_device.name)

View File

@ -135,7 +135,7 @@ RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_tex
it 'allows revocation of an active token' do
visit resource_settings_access_tokens_path
accept_confirm { click_on 'Revoke' }
accept_gl_confirm(button_text: 'Revoke') { click_on 'Revoke' }
expect(page).to have_selector('.settings-message')
expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
@ -156,7 +156,7 @@ RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_tex
it 'allows revocation of an active token' do
visit resource_settings_access_tokens_path
accept_confirm { click_on 'Revoke' }
accept_gl_confirm(button_text: 'Revoke') { click_on 'Revoke' }
expect(page).to have_selector('.settings-message')
expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)

View File

@ -74,7 +74,7 @@ module Quality
end
def pattern(level)
@patterns[level] ||= "#{prefixes_for_pattern}spec/#{folders_pattern(level)}{,/**/}*#{suffix(level)}"
@patterns[level] ||= "#{prefixes_for_pattern}spec/#{folders_pattern(level)}{,/**/}*#{suffix(level)}".freeze # rubocop:disable Style/RedundantFreeze
end
def regexp(level)