Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3e0c035fe3
commit
9440c17f55
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 #
|
||||
##########################################
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
9f87cdc737619e33cddc3ceddc2103dc11dd2577
|
||||
13086e25394b65e4c17eca8484890f62bb2f0b92
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -37,9 +37,7 @@ function monkeyPatchConfirmModal() {
|
|||
Rails.confirm = confirmViaModal;
|
||||
}
|
||||
|
||||
if (gon?.features?.bootstrapConfirmationModals) {
|
||||
monkeyPatchConfirmModal();
|
||||
}
|
||||
|
||||
export const initRails = () => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
|
|
|
@ -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"
|
||||
<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-avatar :size="24" :src="user.avatarUrl" />
|
||||
<span class="gl-pl-2">{{ user.name }}</span>
|
||||
</gl-link>
|
||||
<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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#import "./work_item.fragment.graphql"
|
||||
|
||||
mutation localUpdateWorkItem($input: LocalWorkItemAssigneesInput) {
|
||||
localUpdateWorkItem(input: $input) @client {
|
||||
workItem {
|
||||
...WorkItem
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,8 +16,9 @@ export const temporaryConfig = {
|
|||
WorkItem: {
|
||||
fields: {
|
||||
mockWidgets: {
|
||||
read() {
|
||||
return [
|
||||
read(widgets) {
|
||||
return (
|
||||
widgets || [
|
||||
{
|
||||
__typename: 'LocalWorkItemAssignees',
|
||||
type: 'ASSIGNEES',
|
||||
|
@ -44,7 +48,8 @@ export const temporaryConfig = {
|
|||
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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" } ×
|
||||
.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 & 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'
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' } })
|
||||
|
|
|
@ -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")
|
||||
= 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.")
|
||||
= _('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.')
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
# line_positions_at_source_diff: given the transformed lines,
|
||||
# what are the correct values for old_pos and new_pos?
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# Example: Additions and removals
|
||||
# Old: New:
|
||||
# Original
|
||||
# from | to
|
||||
# A | A
|
||||
# B | D
|
||||
# C | E
|
||||
# F | F
|
||||
#
|
||||
# Original Diff
|
||||
# A A
|
||||
# B D
|
||||
# C E
|
||||
# - B
|
||||
# - C
|
||||
# + D
|
||||
# + 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
|
||||
# Transformed
|
||||
# from | to
|
||||
# A | A
|
||||
# C | D
|
||||
# B | J
|
||||
# L | E
|
||||
# K | K
|
||||
# F | F
|
||||
#
|
||||
# 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 = {}
|
||||
# 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.each do |line|
|
||||
removals[line.old_pos] = line.new_pos unless file_added
|
||||
additions[line.new_pos] = line.old_pos unless file_deleted
|
||||
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
|
||||
|
||||
[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
|
||||
|
||||
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?
|
||||
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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 & 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 ""
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,12 +37,10 @@ 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 }
|
||||
page.all('.labels .js-remove-label').each do |remove|
|
||||
accept_gl_confirm(button_text: 'Delete label') { remove.click }
|
||||
wait_for_requests
|
||||
end
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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: '')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -311,7 +311,6 @@ RSpec.describe 'Pipelines', :js do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(bootstrap_confirmation_modals: false)
|
||||
visit_project_pipelines
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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] },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue