Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-24 15:08:28 +00:00
parent 4b4c254b2c
commit 8e1bb8745b
95 changed files with 3267 additions and 3184 deletions

View File

@ -64,6 +64,7 @@ variables:
NODE_ENV: "test" NODE_ENV: "test"
BUNDLE_WITHOUT: "production:development" BUNDLE_WITHOUT: "production:development"
BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet" BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet"
BUNDLE_FROZEN: "true"
# we override the max_old_space_size to prevent OOM errors # we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584 NODE_OPTIONS: --max_old_space_size=3584
GIT_DEPTH: "20" GIT_DEPTH: "20"

View File

@ -177,6 +177,8 @@ Dangerfile @gl-quality/eng-prod
[Application Security] [Application Security]
/app/assets/javascripts/lib/dompurify.js @gitlab-com/gl-security/appsec /app/assets/javascripts/lib/dompurify.js @gitlab-com/gl-security/appsec
/app/assets/javascripts/gfm_auto_complete.js @gitlab-com/gl-security/appsec
/ee/app/assets/javascripts/gfm_auto_complete.js @gitlab-com/gl-security/appsec
/app/validators/addressable_url_validator.rb @gitlab-com/gl-security/appsec /app/validators/addressable_url_validator.rb @gitlab-com/gl-security/appsec
/app/validators/public_url_validator.rb @gitlab-com/gl-security/appsec /app/validators/public_url_validator.rb @gitlab-com/gl-security/appsec
/config/initializers/content_security_policy.rb @gitlab-com/gl-security/appsec /config/initializers/content_security_policy.rb @gitlab-com/gl-security/appsec

View File

@ -72,5 +72,6 @@ danger-review-local:
reviewers-recommender: reviewers-recommender:
extends: extends:
- .default-retry - .default-retry
- .review:rules:reviewers-recommender
stage: test stage: test
needs: [] needs: []

View File

@ -1663,6 +1663,12 @@
- <<: *if-merge-request - <<: *if-merge-request
changes: *danger-patterns changes: *danger-patterns
.review:rules:reviewers-recommender:
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request
############### ###############
# Setup rules # # Setup rules #
############### ###############

View File

@ -171,7 +171,6 @@ add-jh-folder:
- curl --location -o "jh-folder.tar.gz" "https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab/-/archive/${JH_BRANCH}/gitlab-${JH_BRANCH}.tar.gz?path=jh" - curl --location -o "jh-folder.tar.gz" "https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab/-/archive/${JH_BRANCH}/gitlab-${JH_BRANCH}.tar.gz?path=jh"
- tar -xf "jh-folder.tar.gz" - tar -xf "jh-folder.tar.gz"
- mv "gitlab-${JH_BRANCH}-jh/jh/" ./ - mv "gitlab-${JH_BRANCH}-jh/jh/" ./
- cp Gemfile.lock jh/
- ls -l jh/ - ls -l jh/
artifacts: artifacts:
expire_in: 2d expire_in: 2d

View File

@ -1 +1 @@
803b179a6834fbebcc7886083731bb0f4a67c796 b2c8eaa672c9f2dc4b55477a3876f957e2c9a768

View File

@ -354,7 +354,7 @@ export default {
> >
<header <header
v-if="showToolbar" v-if="showToolbar"
class="row-content-block gl-border-t-0 gl-py-3 gl-display-flex" class="gl-display-flex gl-my-0 gl-text-gray-900"
data-testid="design-toolbar-wrapper" data-testid="design-toolbar-wrapper"
> >
<div <div

View File

@ -32,8 +32,11 @@ export default {
...mapState('pipelines', ['latestPipeline']), ...mapState('pipelines', ['latestPipeline']),
}, },
watch: { watch: {
lastCommit() { lastCommit: {
this.initPipelinePolling(); handler() {
this.initPipelinePolling();
},
immediate: true,
}, },
}, },
mounted() { mounted() {

View File

@ -1,51 +0,0 @@
<script>
import { GlButton, GlCard } from '@gitlab/ui';
import { s__, __ } from '~/locale';
export default {
components: {
GlButton,
GlCard,
},
props: {
upgradePlanPath: {
type: String,
required: false,
default: '',
},
showPremiumMessage: {
type: Boolean,
required: false,
default: false,
},
showUltimateMessage: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
title() {
return this.showUltimateMessage
? this.$options.i18n.titleUltimate
: this.$options.i18n.titlePremium;
},
},
i18n: {
titleUltimate: s__('JiraService|This is an Ultimate feature'),
titlePremium: s__('JiraService|This is a Premium feature'),
content: s__('JiraService|Upgrade your plan to enable this feature of the Jira Integration.'),
upgrade: __('Upgrade your plan'),
},
};
</script>
<template>
<gl-card>
<strong>{{ title }}</strong>
<p>{{ $options.i18n.content }}</p>
<gl-button v-if="upgradePlanPath" category="primary" variant="info" :href="upgradePlanPath">
{{ $options.i18n.upgrade }}
</gl-button>
</gl-card>
</template>

View File

@ -21,7 +21,6 @@ function parseDatasetToProps(data) {
type, type,
commentDetail, commentDetail,
projectKey, projectKey,
upgradePlanPath,
learnMorePath, learnMorePath,
aboutPricingUrl, aboutPricingUrl,
triggerEvents, triggerEvents,
@ -80,7 +79,6 @@ function parseDatasetToProps(data) {
initialEnableJiraVulnerabilities: enableJiraVulnerabilities, initialEnableJiraVulnerabilities: enableJiraVulnerabilities,
initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype, initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype,
initialProjectKey: projectKey, initialProjectKey: projectKey,
upgradePlanPath,
}, },
learnMorePath, learnMorePath,
aboutPricingUrl, aboutPricingUrl,

View File

@ -2,7 +2,11 @@
import { GlButton, GlLink, GlIcon } from '@gitlab/ui'; import { GlButton, GlLink, GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { TRIGGER_ELEMENT_BUTTON, TRIGGER_ELEMENT_SIDE_NAV } from '../constants'; import {
TRIGGER_ELEMENT_BUTTON,
TRIGGER_ELEMENT_SIDE_NAV,
TRIGGER_DEFAULT_QA_SELECTOR,
} from '../constants';
export default { export default {
components: { GlButton, GlLink, GlIcon }, components: { GlButton, GlLink, GlIcon },
@ -46,12 +50,17 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
qaSelector: {
type: String,
required: false,
default: TRIGGER_DEFAULT_QA_SELECTOR,
},
}, },
computed: { computed: {
componentAttributes() { componentAttributes() {
const baseAttributes = { const baseAttributes = {
class: this.classes, class: this.classes,
'data-qa-selector': 'invite_members_button', 'data-qa-selector': this.qaSelector,
'data-test-id': 'invite-members-button', 'data-test-id': 'invite-members-button',
}; };

View File

@ -18,6 +18,7 @@ export const USERS_FILTER_ALL = 'all';
export const USERS_FILTER_SAML_PROVIDER_ID = 'saml_provider_id'; export const USERS_FILTER_SAML_PROVIDER_ID = 'saml_provider_id';
export const TRIGGER_ELEMENT_BUTTON = 'button'; export const TRIGGER_ELEMENT_BUTTON = 'button';
export const TRIGGER_ELEMENT_SIDE_NAV = 'side-nav'; export const TRIGGER_ELEMENT_SIDE_NAV = 'side-nav';
export const TRIGGER_DEFAULT_QA_SELECTOR = 'invite_members_button';
export const MEMBERS_MODAL_DEFAULT_TITLE = s__('InviteMembersModal|Invite members'); export const MEMBERS_MODAL_DEFAULT_TITLE = s__('InviteMembersModal|Invite members');
export const MEMBERS_MODAL_CELEBRATE_TITLE = s__( export const MEMBERS_MODAL_CELEBRATE_TITLE = s__(
'InviteMembersModal|GitLab is better with colleagues!', 'InviteMembersModal|GitLab is better with colleagues!',

View File

@ -11,6 +11,7 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { mergeUrlParams } from '~/lib/utils/url_utility'; import { mergeUrlParams } from '~/lib/utils/url_utility';
import api from '~/api';
// Todo: Remove this when fixing issue in input_setter plugin // Todo: Remove this when fixing issue in input_setter plugin
const InputSetter = { ...ISetter }; const InputSetter = { ...ISetter };
@ -149,7 +150,7 @@ export default class CreateMergeRequestDropdown {
}); });
} }
createBranch() { createBranch(navigateToBranch = true) {
this.isCreatingBranch = true; this.isCreatingBranch = true;
return axios return axios
@ -158,7 +159,10 @@ export default class CreateMergeRequestDropdown {
}) })
.then(({ data }) => { .then(({ data }) => {
this.branchCreated = true; this.branchCreated = true;
window.location.href = data.url;
if (navigateToBranch) {
window.location.href = data.url;
}
}) })
.catch(() => .catch(() =>
createFlash({ createFlash({
@ -170,23 +174,25 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() { createMergeRequest() {
return new Promise(() => { return new Promise(() => {
this.isCreatingMergeRequest = true; this.isCreatingMergeRequest = true;
return this.createBranch().then(() => { return this.createBranch(false)
let path = canCreateConfidentialMergeRequest() .then(() => api.trackRedisHllUserEvent('i_code_review_user_create_mr_from_issue'))
? this.createMrPath.replace( .then(() => {
this.projectPath, let path = canCreateConfidentialMergeRequest()
confidentialMergeRequestState.selectedProject.pathWithNamespace, ? this.createMrPath.replace(
) this.projectPath,
: this.createMrPath; confidentialMergeRequestState.selectedProject.pathWithNamespace,
path = mergeUrlParams( )
{ : this.createMrPath;
'merge_request[target_branch]': this.refInput.value, path = mergeUrlParams(
'merge_request[source_branch]': this.branchInput.value, {
}, 'merge_request[target_branch]': this.refInput.value,
path, 'merge_request[source_branch]': this.branchInput.value,
); },
path,
);
window.location.href = path; window.location.href = path;
}); });
}); });
} }

View File

@ -42,10 +42,6 @@ body {
text-align: left; text-align: left;
background-color: #1f1f1f; background-color: #1f1f1f;
} }
h1 {
margin-top: 0;
margin-bottom: 0.25rem;
}
ul { ul {
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
@ -105,15 +101,6 @@ button::-moz-focus-inner,
[type="search"] { [type="search"] {
outline-offset: -2px; outline-offset: -2px;
} }
h1 {
margin-bottom: 0.25rem;
font-weight: 600;
line-height: 1.2;
color: #fafafa;
}
h1 {
font-size: 2.1875rem;
}
.list-unstyled { .list-unstyled {
padding-left: 0; padding-left: 0;
list-style: none; list-style: none;
@ -547,10 +534,6 @@ html [type="button"],
[role="button"] { [role="button"] {
cursor: pointer; cursor: pointer;
} }
h1 {
margin-top: 20px;
margin-bottom: 10px;
}
strong { strong {
font-weight: bold; font-weight: bold;
} }

View File

@ -27,10 +27,6 @@ body {
text-align: left; text-align: left;
background-color: #fff; background-color: #fff;
} }
h1 {
margin-top: 0;
margin-bottom: 0.25rem;
}
ul { ul {
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
@ -90,15 +86,6 @@ button::-moz-focus-inner,
[type="search"] { [type="search"] {
outline-offset: -2px; outline-offset: -2px;
} }
h1 {
margin-bottom: 0.25rem;
font-weight: 600;
line-height: 1.2;
color: #303030;
}
h1 {
font-size: 2.1875rem;
}
.list-unstyled { .list-unstyled {
padding-left: 0; padding-left: 0;
list-style: none; list-style: none;
@ -532,10 +519,6 @@ html [type="button"],
[role="button"] { [role="button"] {
cursor: pointer; cursor: pointer;
} }
h1 {
margin-top: 20px;
margin-bottom: 10px;
}
strong { strong {
font-weight: bold; font-weight: bold;
} }

View File

@ -139,7 +139,7 @@ module MergeRequestsHelper
end end
def toggle_draft_merge_request_path(issuable) def toggle_draft_merge_request_path(issuable)
wip_event = issuable.work_in_progress? ? 'unwip' : 'wip' wip_event = issuable.draft? ? 'unwip' : 'wip'
issuable_path(issuable, { merge_request: { wip_event: wip_event } }) issuable_path(issuable, { merge_request: { wip_event: wip_event } })
end end

View File

@ -519,9 +519,10 @@ class Commit
# #
DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze
def work_in_progress? def draft?
!!(title =~ DRAFT_REGEX) !!(title =~ DRAFT_REGEX)
end end
alias_method :work_in_progress?, :draft?
def merged_merge_request?(user) def merged_merge_request?(user)
!!merged_merge_request(user) !!merged_merge_request(user)

View File

@ -18,7 +18,8 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
expose :rebase_in_progress?, as: :rebase_in_progress expose :rebase_in_progress?, as: :rebase_in_progress
expose :commits_count expose :commits_count
expose :merge_ongoing?, as: :merge_ongoing expose :merge_ongoing?, as: :merge_ongoing
expose :work_in_progress?, as: :work_in_progress expose :draft?, as: :draft
expose :draft?, as: :work_in_progress
expose :cannot_be_merged?, as: :has_conflicts expose :cannot_be_merged?, as: :has_conflicts
expose :can_be_merged?, as: :can_be_merged expose :can_be_merged?, as: :can_be_merged
expose :remove_source_branch?, as: :remove_source_branch expose :remove_source_branch?, as: :remove_source_branch

View File

@ -54,7 +54,7 @@ module Issuable
def create_draft_note(old_title) def create_draft_note(old_title)
return unless issuable.is_a?(MergeRequest) return unless issuable.is_a?(MergeRequest)
if MergeRequest.work_in_progress?(old_title) != issuable.work_in_progress? if MergeRequest.draft?(old_title) != issuable.draft?
SystemNoteService.handle_merge_request_draft(issuable, issuable.project, current_user) SystemNoteService.handle_merge_request_draft(issuable, issuable.project, current_user)
end end
end end

View File

@ -255,17 +255,17 @@ module MergeRequests
commit_shas = merge_request.commit_shas commit_shas = merge_request.commit_shas
wip_commit = @commits.detect do |commit| draft_commit = @commits.detect do |commit|
commit.work_in_progress? && commit_shas.include?(commit.sha) commit.draft? && commit_shas.include?(commit.sha)
end end
if wip_commit && !merge_request.work_in_progress? if draft_commit && !merge_request.draft?
merge_request.update(title: merge_request.wip_title) merge_request.update(title: merge_request.draft_title)
SystemNoteService.add_merge_request_draft_from_commit( SystemNoteService.add_merge_request_draft_from_commit(
merge_request, merge_request,
merge_request.project, merge_request.project,
@current_user, @current_user,
wip_commit draft_commit
) )
end end
end end

View File

@ -180,16 +180,16 @@ module MergeRequests
return unless changed_fields.include?("title") return unless changed_fields.include?("title")
old_title, new_title = merge_request.previous_changes["title"] old_title, new_title = merge_request.previous_changes["title"]
old_title_wip = MergeRequest.work_in_progress?(old_title) old_title_draft = MergeRequest.draft?(old_title)
new_title_wip = MergeRequest.work_in_progress?(new_title) new_title_draft = MergeRequest.draft?(new_title)
if !old_title_wip && new_title_wip if !old_title_draft && new_title_draft
# Marked as Draft/WIP # Marked as Draft
# #
merge_request_activity_counter merge_request_activity_counter
.track_marked_as_draft_action(user: current_user) .track_marked_as_draft_action(user: current_user)
elsif old_title_wip && !new_title_wip elsif old_title_draft && !new_title_draft
# Unmarked as Draft/WIP # Unmarked as Draft
# #
notify_draft_status_changed(merge_request) notify_draft_status_changed(merge_request)

View File

@ -27,7 +27,7 @@ module SystemNotes
end end
def handle_merge_request_draft def handle_merge_request_draft
action = noteable.work_in_progress? ? "draft" : "ready" action = noteable.draft? ? "draft" : "ready"
body = "marked this merge request as **#{action}**" body = "marked this merge request as **#{action}**"

View File

@ -1,7 +1,8 @@
.js-invite-members-trigger{ data: { trigger_source: 'group-side-nav', .js-invite-members-trigger{ data: { trigger_source: 'group-side-nav',
icon: 'users', icon: 'users',
display_text: title, display_text: title,
trigger_element: 'side-nav'} } trigger_element: 'side-nav',
qa_selector: 'invite_members_sidebar_button' } }
= render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu } = render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu }
= render 'groups/invite_members_modal', group: group = render 'groups/invite_members_modal', group: group

View File

@ -6,7 +6,7 @@
.container-fluid .container-fluid
.header-content.js-header-content .header-content.js-header-content
.title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0 .title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0
%h1.title .title
%span.gl-sr-only GitLab %span.gl-sr-only GitLab
= link_to root_path, title: _('Dashboard'), id: 'logo', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do = link_to root_path, title: _('Dashboard'), id: 'logo', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
= brand_header_logo = brand_header_logo

View File

@ -1,7 +1,8 @@
.js-invite-members-trigger{ data: { trigger_source: 'project-side-nav', .js-invite-members-trigger{ data: { trigger_source: 'project-side-nav',
icon: 'users', icon: 'users',
display_text: title, display_text: title,
trigger_element: 'side-nav'} } trigger_element: 'side-nav',
qa_selector: 'invite_members_sidebar_button' } }
= render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu } = render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu }
= render 'projects/invite_members_modal', project: project = render 'projects/invite_members_modal', project: project

View File

@ -20,7 +20,7 @@
%li.gl-new-dropdown-item %li.gl-new-dropdown-item
= link_to toggle_draft_merge_request_path(@merge_request), method: :put, class: 'dropdown-item js-draft-toggle-button' do = link_to toggle_draft_merge_request_path(@merge_request), method: :put, class: 'dropdown-item js-draft-toggle-button' do
.gl-new-dropdown-item-text-wrapper .gl-new-dropdown-item-text-wrapper
= @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft') = @merge_request.draft? ? _('Mark as ready') : _('Mark as draft')
%li.gl-new-dropdown-item.js-close-item %li.gl-new-dropdown-item.js-close-item
= link_to close_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do = link_to close_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
.gl-new-dropdown-item-text-wrapper .gl-new-dropdown-item-text-wrapper

View File

@ -22,7 +22,7 @@
= _('Title') = _('Title')
%i{ aria: { hidden: true } }= '*' %i{ aria: { hidden: true } }= '*'
= render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?) = render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:draft?)
#js-suggestions{ data: { project_path: @project.full_path } } #js-suggestions{ data: { project_path: @project.full_path } }
= render 'shared/issuable/form/type_selector', issuable: issuable, form: form = render 'shared/issuable/form/type_selector', issuable: issuable, form: form

View File

@ -12,7 +12,7 @@
= form.text_field :title, required: true, aria: { required: true }, maxlength: 255, autofocus: true, = form.text_field :title, required: true, aria: { required: true }, maxlength: 255, autofocus: true,
autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title'), dir: 'auto' autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title'), dir: 'auto'
- if issuable.respond_to?(:work_in_progress?) - if issuable.respond_to?(:draft?)
.form-text.text-muted .form-text.text-muted
.js-wip-explanation{ style: "display: none;" } .js-wip-explanation{ style: "display: none;" }
= remove_wip_text = remove_wip_text

View File

@ -2749,15 +2749,6 @@
:weight: 1 :weight: 1
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: project_daily_statistics
:worker_name: ProjectDailyStatisticsWorker
:feature_category: :source_code_management
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: project_destroy - :name: project_destroy
:worker_name: ProjectDestroyWorker :worker_name: ProjectDestroyWorker
:feature_category: :source_code_management :feature_category: :source_code_management

View File

@ -1,20 +0,0 @@
# frozen_string_literal: true
# Deprecated: https://gitlab.com/gitlab-org/gitlab/-/issues/214585
class ProjectDailyStatisticsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
data_consistency :always
sidekiq_options retry: 3
feature_category :source_code_management
def perform(project_id)
project = Project.find_by_id(project_id)
return unless project&.repository&.exists?
Projects::FetchStatisticsIncrementService.new(project).execute
end
end

View File

@ -345,8 +345,6 @@
- 3 - 3
- - project_cache - - project_cache
- 1 - 1
- - project_daily_statistics
- 1
- - project_destroy - - project_destroy
- 1 - 1
- - project_export - - project_export

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DropIndexNamespacesOnName < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_namespaces_on_name'
def up
remove_concurrent_index_by_name :namespaces, INDEX_NAME, if_exists: true
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
4d44b5cd1bcd731e841849c65f64a19960fa13cd90d1bda023e797db20b8b8c6

View File

@ -20,6 +20,8 @@ are stored securely outside of your project's repository, and are not version co
It is safe to store sensitive information in these files. Secure files support both It is safe to store sensitive information in these files. Secure files support both
plain text and binary file types. plain text and binary file types.
You can manage secure files in the project settings, or with the [secure files API](../../api/secure_files.md).
Secure files can be [downloaded and used by CI/CD jobs](#use-secure-files-in-cicd-jobs) Secure files can be [downloaded and used by CI/CD jobs](#use-secure-files-in-cicd-jobs)
by using the [load-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/devops-for-mobile-apps/load-secure-files) by using the [load-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/devops-for-mobile-apps/load-secure-files)
tool. tool.
@ -30,49 +32,14 @@ Additional features and capabilities are planned.
## Add a secure file to a project ## Add a secure file to a project
To add a secure file to a project, use the [Secure Files API](../../api/secure_files.md#create-secure-file). To add a secure file to a project:
Send a POST request to the secure files endpoint for your project: 1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
```shell 1. In the **Secure Files** section, select **Manage**.
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \ 1. Select **Upload File**.
"https://gitlab.example.com/api/v4/projects/:project_id/secure_files" --form "name=myfile.jks" --form "file=@/path/to/file/myfile.jks" 1. Find the file to upload, select **Open**, and the file upload begins immediately.
``` The file shows up in the list when the upload is complete.
The response returns all of the metadata for the file you just uploaded. For example:
```json
{
"id": 1,
"name": "myfile.jks",
"checksum": "16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac",
"checksum_algorithm": "sha256",
"created_at": "2022-02-22T22:22:22.222Z"
}
```
## List all secure files in a project
To list all secure files in a project, use the [Secure Files API](../../api/secure_files.md#list-project-secure-files).
Send a GET request to the secure files endpoint for your project:
```shell
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/projects/:project_id/secure_files"
```
The response returns an array of all of the secure files in the project. For example:
```json
[{
"id": 1,
"name": "myfile.jks",
"checksum": "16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac",
"checksum_algorithm": "sha256",
"created_at": "2022-02-22T22:22:22.222Z"
}]
```
## Use secure files in CI/CD jobs ## Use secure files in CI/CD jobs

View File

@ -213,32 +213,42 @@ HTML which differs from the canonical HTML examples from the specification.
For every Markdown example in the GLFM specification, three For every Markdown example in the GLFM specification, three
versions of HTML can potentially be rendered from the example: versions of HTML can potentially be rendered from the example:
1. **Static HTML**: HTML produced by the backend (Ruby) renderer, which - Static HTML.
contains extra styling and behavioral HTML. For example, **Create task** buttons - WYSIWYG HTML.
added for dynamically creating an issue from a task list item. - Canonical HTML.
The GitLab [Markdown API](../../../api/markdown.md) generates HTML
for a given Markdown string using this method.
1. **WYSIWYG HTML**: HTML produced by the frontend (JavaScript) Content Editor,
which includes parsing and rendering logic. Used to present an editable document
in the ProseMirror WYSIWYG editor.
1. **Canonical HTML**: The clean, basic version of HTML rendered from Markdown.
1. For the examples which come from the CommonMark specification and
GFM extensions specification,
the canonical HTML is the exact identical HTML found in the
GFM
`spec.txt` example blocks.
1. For GLFM extensions to the <abbr title="GitHub Flavored Markdown">GFM</abbr> / CommonMark
specification, a `glfm_canonical_examples.txt`
[input specification file](#input-specification-files) contains the
Markdown examples and corresponding canonical HTML examples.
As the rendered static and WYSIWYG HTML from the backend (Ruby) and frontend (JavaScript) #### Static HTML
renderers contain extra HTML, their rendered HTML can be converted to canonical HTML
by a [canonicalization](#canonicalization-of-html) process.
#### Canonicalization of HTML **Static HTML** is HTML produced by the backend (Ruby) renderer, which
contains extra styling and behavioral HTML. For example, **Create task** buttons
added for dynamically creating an issue from a task list item.
The GitLab [Markdown API](../../../api/markdown.md) generates HTML
for a given Markdown string using this method.
Neither the backend (Ruby) nor the frontend (JavaScript) rendered can directly render canonical HTML. #### WYSIWYG HTML
**WYSIWYG HTML** is HTML produced by the frontend (JavaScript) Content Editor,
which includes parsing and rendering logic. It is used to present an editable document
in the ProseMirror WYSIWYG editor.
#### Canonical HTML
**Canonical HTML** is the clean, basic version of HTML rendered from Markdown.
1. For the examples which come from the CommonMark specification and
GFM extensions specification, the canonical HTML is the exact identical HTML found in the
GFM `spec.txt` example blocks.
1. For GLFM extensions to the <abbr title="GitHub Flavored Markdown">GFM</abbr> / CommonMark
specification, a `glfm_canonical_examples.txt` [input specification file](#input-specification-files)
contains the Markdown examples and corresponding canonical HTML examples.
### Canonicalization of HTML
The rendered [static HTML](#static-html) and [WYSIWYG HTML](#wysiwyg-html)
from the backend (Ruby) and frontend (JavaScript) renderers usually contains extra styling
or HTML elements, to support specific appearance and behavioral requirements.
Neither the backend nor the frontend rendering logic can directly render the clean, basic canonical HTML.
Nor should they be able to, because: Nor should they be able to, because:
- It's not a direct requirement to support any GitLab application feature. - It's not a direct requirement to support any GitLab application feature.
@ -578,23 +588,57 @@ The actual file should not have these prefixed `|` characters.
controls the behavior of the [scripts](#scripts) and [tests](#types-of-markdown-tests-driven-by-the-glfm-specification). controls the behavior of the [scripts](#scripts) and [tests](#types-of-markdown-tests-driven-by-the-glfm-specification).
- It is manually updated. - It is manually updated.
- It controls the status of automatic generation of files based on Markdown examples. - The `skip_update_example_snapshot*` fields control the status of automatic generation of
- It allows example snapshot generation, Markdown conformance tests, or snapshot example entries based on Markdown examples.
Markdown snapshot tests to be skipped for individual examples. For example, if - The `skip_running_*` control allow Markdown conformance tests or
they are unimplemented, broken, or cannot be tested for some reason. Markdown snapshot tests to be skipped for individual examples.
- This allows control over skipping this processing or testing of various examples when they
are unimplemented, partially implemented, broken, or cannot be generated or tested for some reason.
- All entries default to false. They can be set to true by specifying a Ruby
value which evaluates as truthy. This could be the boolean `true` value, but ideally should
be a string describing why the example's updating or testing is being skipped.
- When a `skip_update_example_snapshot*` entry is true, the existing value is preserved.
However, since the YAML is re-written, the style of the string value and its
[Block Chomping Indicator (`|`)](https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator)
may be modified, because the Ruby `psych` YAML library automatically determines this.
The following optional entries are supported for each example. They all default to `false`:
- `skip_update_example_snapshots`: When true, skips any addition or update of any this example's entries
in the [`spec/fixtures/glfm/example_snapshots/html.yml`](#specfixturesglfmexample_snapshotshtmlyml) file
or the [`spec/fixtures/glfm/example_snapshots/prosemirror_json.yml`](#specfixturesglfmexample_snapshotsprosemirror_jsonyml) file.
If this value is truthy, then no other `skip_update_example_snapshot_*` entries can be truthy,
and an error is raised if any of them are.
- `skip_update_example_snapshot_html_static`: When true, skips addition or update of this example's [static HTML](#static-html)
entry in the [`spec/fixtures/glfm/example_snapshots/html.yml`](#specfixturesglfmexample_snapshotshtmlyml) file.
- `skip_update_example_snapshot_html_wysiwyg`: When true, skips addition or update of this example's [WYSIWYG HTML](#wysiwyg-html)
entry in the [`spec/fixtures/glfm/example_snapshots/html.yml`](#specfixturesglfmexample_snapshotshtmlyml) file.
- `skip_update_example_snapshot_prosemirror_json`: When true, skips addition or update of this example's
entry in the [`spec/fixtures/glfm/example_snapshots/prosemirror_json.yml`](#specfixturesglfmexample_snapshotsprosemirror_jsonyml) file.
- `skip_running_conformance_static_tests`: When true, skips running the [Markdown conformance tests](#markdown-conformance-testing)
of the [static HTML](#static-html) for this example.
- `skip_running_conformance_wysiwyg_tests`: When true, skips running the [Markdown conformance tests](#markdown-conformance-testing)
of the [WYSIWYG HTML](#wysiwyg-html) for this example.
- `skip_running_snapshot_static_html_tests`: When true, skips running the [Markdown snapshot tests](#markdown-snapshot-testing)
of the [static HTML](#multiple-versions-of-rendered-html) for this example.
- `skip_running_snapshot_wysiwyg_html_tests`: When true, skips running the [Markdown snapshot tests](#markdown-snapshot-testing)
of the [WYSIWYG HTML](#wysiwyg-html) for this example.
- `skip_running_snapshot_prosemirror_json_tests`: When true, skips running the [Markdown snapshot tests](#markdown-snapshot-testing)
of the [ProseMirror JSON](#specfixturesglfmexample_snapshotsprosemirror_jsonyml) for this example.
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` sample entry: `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` sample entry:
```yaml ```yaml
07_99_an_example_with_incomplete_wysiwyg_implementation_1: 07_99_an_example_with_incomplete_wysiwyg_implementation_1:
skip_update_example_snapshots: false skip_update_example_snapshots: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_static: false skip_update_example_snapshot_html_static: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_wysiwyg: false skip_update_example_snapshot_html_wysiwyg: 'An explanation of the reason for skipping.'
skip_running_conformance_static_tests: false skip_update_example_snapshot_prosemirror_json: 'An explanation of the reason for skipping.'
skip_running_conformance_wysiwyg_tests: false skip_running_conformance_static_tests: 'An explanation of the reason for skipping.'
skip_running_snapshot_static_html_tests: false skip_running_conformance_wysiwyg_tests: 'An explanation of the reason for skipping.'
skip_running_snapshot_wysiwyg_html_tests: false skip_running_snapshot_static_html_tests: 'An explanation of the reason for skipping.'
skip_running_snapshot_prosemirror_json_tests: false skip_running_snapshot_wysiwyg_html_tests: 'An explanation of the reason for skipping.'
skip_running_snapshot_prosemirror_json_tests: 'An explanation of the reason for skipping.'
``` ```
#### Output specification files #### Output specification files

View File

@ -22,9 +22,17 @@ it should be restricted on namespace scope.
1. Check using: 1. Check using:
```ruby ```ruby
project.feature_available?(:feature_symbol) project.licensed_feature_available?(:feature_symbol)
``` ```
or
```ruby
group.licensed_feature_available?(:feature_symbol)
```
For projects, `licensed_feature_available` delegates to its associated `namespace`.
## Restricting global features (instance) ## Restricting global features (instance)
However, for features such as [Geo](../administration/geo/index.md) and However, for features such as [Geo](../administration/geo/index.md) and

View File

@ -52,9 +52,9 @@ work it needs to perform and how long it takes to complete:
- Clean-ups, like removing unused columns. - Clean-ups, like removing unused columns.
- Adding non-critical indices on high-traffic tables. - Adding non-critical indices on high-traffic tables.
- Adding non-critical indices that take a long time to create. - Adding non-critical indices that take a long time to create.
1. [**Background migrations.**](database/background_migrations.md) These aren't regular Rails migrations, but application code that is 1. [**Batched background migrations.**](database/batched_background_migrations.md) These aren't regular Rails migrations, but application code that is
executed via Sidekiq jobs, although a post-deployment migration is used to schedule them. Use them only for data migrations that executed via Sidekiq jobs, although a post-deployment migration is used to schedule them. Use them only for data migrations that
exceed the timing guidelines for post-deploy migrations. Background migrations should _not_ change the schema. exceed the timing guidelines for post-deploy migrations. Batched background migrations should _not_ change the schema.
Use the following diagram to guide your decision, but keep in mind that it is just a tool, and Use the following diagram to guide your decision, but keep in mind that it is just a tool, and
the final outcome will always be dependent on the specific changes being made: the final outcome will always be dependent on the specific changes being made:

View File

@ -328,19 +328,20 @@ limiting responses](#rate-limiting-responses).
The following table describes the rate limits for GitLab.com, both before and The following table describes the rate limits for GitLab.com, both before and
after the limits change in January, 2021: after the limits change in January, 2021:
| Rate limit | From 2021-02-12 | From 2022-02-03 | | Rate limit | From 2021-02-12 | From 2022-02-03 |
|:--------------------------------------------------------------------------|:------------------------------|:----------------------------------------| |:---------------------------------------------------------------------------|:------------------------------|:----------------------------------------|
| **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute | | **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute |
| **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute | | **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute |
| **Unauthenticated** traffic (from a given **IP address**) | **500** requests per minute | **500** requests per minute | | **Unauthenticated** traffic (from a given **IP address**) | **500** requests per minute | **500** requests per minute |
| **Authenticated** API traffic (for a given **user**) | **2,000** requests per minute | **2,000** requests per minute | | **Authenticated** API traffic (for a given **user**) | **2,000** requests per minute | **2,000** requests per minute |
| **Authenticated** non-API HTTP traffic (for a given **user**) | **1,000** requests per minute | **1,000** requests per minute | | **Authenticated** non-API HTTP traffic (for a given **user**) | **1,000** requests per minute | **1,000** requests per minute |
| **All** traffic (from a given **IP address**) | **2,000** requests per minute | **2,000** requests per minute | | **All** traffic (from a given **IP address**) | **2,000** requests per minute | **2,000** requests per minute |
| **Issue creation** | **300** requests per minute | **200** requests per minute | | **Issue creation** | **300** requests per minute | **200** requests per minute |
| **Note creation** (on issues and merge requests) | **60** requests per minute | **60** requests per minute | | **Note creation** (on issues and merge requests) | **60** requests per minute | **60** requests per minute |
| **Advanced, project, and group search** API (for a given **IP address**) | **10** requests per minute | **10** requests per minute | | **Advanced, project, and group search** API (for a given **IP address**) | **10** requests per minute | **10** requests per minute |
| **GitLab Pages** requests (for a given **IP address**) | | **1000** requests per **50 seconds** | | **GitLab Pages** requests (for a given **IP address**) | | **1000** requests per **50 seconds** |
| **GitLab Pages** requests (for a given **GitLab Pages domain**) | | **5000** requests per **10 seconds** | | **GitLab Pages** requests (for a given **GitLab Pages domain**) | | **5000** requests per **10 seconds** |
| **Pipeline creation** requests (for a given **project, user, and commit**) | | **25** requests per minute |
More details are available on the rate limits for [protected More details are available on the rate limits for [protected
paths](#protected-paths-throttle) and [raw paths](#protected-paths-throttle) and [raw

View File

@ -1,17 +1,14 @@
--- ---
- 06_05__inlines__emphasis_and_strong_emphasis__37: 02_01__preliminaries__tabs__001:
skip_update_example_snapshots: 'Psych YAML library has a problem with dumping "5__6__78", it thinks its an invalid integer' # NOTE: False values are optional, they are only included here for reference. See
- 07_01_first_gitlab_specific_section_with_examples_strong_but_with_two_asterisks: # https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#glfm_example_statusyml
# for more details.
skip_update_example_snapshots: false skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false skip_update_example_snapshot_html_static: false
skip_running_snapshot_wysiwyg_html_tests: false skip_update_example_snapshot_html_wysiwyg: false
skip_running_snapshot_prosemirror_json_tests: false skip_update_example_snapshot_prosemirror_json: false
skip_running_conformance_static_tests: false skip_running_conformance_static_tests: false # NOT YET SUPPORTED
skip_running_conformance_wysiwyg_tests: false skip_running_conformance_wysiwyg_tests: false # NOT YET SUPPORTED
- 07_02_first_gitlab_specific_section_with_examples_strong_but_with_html: skip_running_snapshot_static_html_tests: false # NOT YET SUPPORTED
skip_update_example_snapshots: false skip_running_snapshot_wysiwyg_html_tests: false # NOT YET SUPPORTED
skip_running_snapshot_static_html_tests: false skip_running_snapshot_prosemirror_json_tests: false # NOT YET SUPPORTED
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false

View File

@ -169,7 +169,6 @@ module API
:merge_commit_template, :merge_commit_template,
:squash_commit_template, :squash_commit_template,
:repository_storage, :repository_storage,
:compliance_framework_setting,
:packages_enabled, :packages_enabled,
:service_desk_enabled, :service_desk_enabled,
:keep_latest_artifact, :keep_latest_artifact,

View File

@ -52,7 +52,7 @@ module Gitlab
source: merge_request.source_project.try(:hook_attrs), source: merge_request.source_project.try(:hook_attrs),
target: merge_request.target_project.hook_attrs, target: merge_request.target_project.hook_attrs,
last_commit: merge_request.diff_head_commit&.hook_attrs, last_commit: merge_request.diff_head_commit&.hook_attrs,
work_in_progress: merge_request.work_in_progress?, work_in_progress: merge_request.draft?,
total_time_spent: merge_request.total_time_spent, total_time_spent: merge_request.total_time_spent,
time_change: merge_request.time_change, time_change: merge_request.time_change,
human_total_time_spent: merge_request.human_total_time_spent, human_total_time_spent: merge_request.human_total_time_spent,

View File

@ -91,7 +91,7 @@ module Gitlab
desc 'Toggle the Draft status' desc 'Toggle the Draft status'
explanation do explanation do
noun = quick_action_target.to_ability_name.humanize(capitalize: false) noun = quick_action_target.to_ability_name.humanize(capitalize: false)
if quick_action_target.work_in_progress? if quick_action_target.draft?
_("Unmarks this %{noun} as a draft.") _("Unmarks this %{noun} as a draft.")
else else
_("Marks this %{noun} as a draft.") _("Marks this %{noun} as a draft.")
@ -99,7 +99,7 @@ module Gitlab
end end
execution_message do execution_message do
noun = quick_action_target.to_ability_name.humanize(capitalize: false) noun = quick_action_target.to_ability_name.humanize(capitalize: false)
if quick_action_target.work_in_progress? if quick_action_target.draft?
_("Unmarked this %{noun} as a draft.") _("Unmarked this %{noun} as a draft.")
else else
_("Marked this %{noun} as a draft.") _("Marked this %{noun} as a draft.")
@ -108,12 +108,13 @@ module Gitlab
types MergeRequest types MergeRequest
condition do condition do
quick_action_target.respond_to?(:work_in_progress?) && quick_action_target.respond_to?(:draft?) &&
# Allow it to mark as WIP on MR creation page _or_ through MR notes. # Allow it to mark as draft on MR creation page or through MR notes
#
(quick_action_target.new_record? || current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target)) (quick_action_target.new_record? || current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target))
end end
command :draft do command :draft do
@updates[:wip_event] = quick_action_target.work_in_progress? ? 'unwip' : 'wip' @updates[:wip_event] = quick_action_target.draft? ? 'unwip' : 'wip'
end end
desc _('Set target branch') desc _('Set target branch')

View File

@ -7684,6 +7684,9 @@ msgstr ""
msgid "Checkout|Name of company or organization using GitLab" msgid "Checkout|Name of company or organization using GitLab"
msgstr "" msgstr ""
msgid "Checkout|Name: %{errors}"
msgstr ""
msgid "Checkout|Need more users? Purchase GitLab for your %{company}." msgid "Checkout|Need more users? Purchase GitLab for your %{company}."
msgstr "" msgstr ""
@ -21828,18 +21831,9 @@ msgstr ""
msgid "JiraService|This feature requires a Premium plan." msgid "JiraService|This feature requires a Premium plan."
msgstr "" msgstr ""
msgid "JiraService|This is a Premium feature"
msgstr ""
msgid "JiraService|This is an Ultimate feature"
msgstr ""
msgid "JiraService|Transition Jira issues to their final state:" msgid "JiraService|Transition Jira issues to their final state:"
msgstr "" msgstr ""
msgid "JiraService|Upgrade your plan to enable this feature of the Jira Integration."
msgstr ""
msgid "JiraService|Use Jira as this project's issue tracker." msgid "JiraService|Use Jira as this project's issue tracker."
msgstr "" msgstr ""
@ -40735,9 +40729,6 @@ msgstr ""
msgid "Upgrade offers available!" msgid "Upgrade offers available!"
msgstr "" msgstr ""
msgid "Upgrade your plan"
msgstr ""
msgid "Upload" msgid "Upload"
msgstr "" msgstr ""

View File

@ -27,7 +27,7 @@ module QA
element :invite_a_group_button element :invite_a_group_button
end end
base.view 'app/assets/javascripts/invite_members/components/invite_members_trigger.vue' do base.view 'app/assets/javascripts/invite_members/constants.js' do
element :invite_members_button element :invite_members_button
end end
end end

View File

@ -15,7 +15,7 @@ module QA
element :invite_a_group_button element :invite_a_group_button
end end
view 'app/assets/javascripts/invite_members/components/invite_members_trigger.vue' do view 'app/assets/javascripts/invite_members/constants.js' do
element :invite_members_button element :invite_members_button
end end

View File

@ -13,6 +13,10 @@ require_relative 'parse_examples'
# for details on the implementation and usage of this script. This developers guide # for details on the implementation and usage of this script. This developers guide
# contains diagrams and documentation of this script, # contains diagrams and documentation of this script,
# including explanations and examples of all files it reads and writes. # including explanations and examples of all files it reads and writes.
#
# Also note that this script is intentionally written in a pure-functional (not OO) style,
# with no dependencies on Rails or the GitLab libraries. These choices are intended to make
# it faster and easier to test and debug.
module Glfm module Glfm
class UpdateExampleSnapshots class UpdateExampleSnapshots
include Constants include Constants
@ -27,28 +31,20 @@ module Glfm
output('(Skipping static HTML generation)') if skip_static_and_wysiwyg output('(Skipping static HTML generation)') if skip_static_and_wysiwyg
glfm_spec_txt_lines, _glfm_examples_status_lines = read_input_files output("Reading #{GLFM_SPEC_TXT_PATH}...")
glfm_spec_txt_lines = File.open(GLFM_SPEC_TXT_PATH).readlines
# Parse all the examples from `spec.txt`, using a Ruby port of the Python `get_tests` # Parse all the examples from `spec.txt`, using a Ruby port of the Python `get_tests`
# function the from original CommonMark/GFM `spec_test.py` script. # function the from original CommonMark/GFM `spec_test.py` script.
all_examples = parse_examples(glfm_spec_txt_lines) all_examples = parse_examples(glfm_spec_txt_lines)
add_example_names(all_examples) add_example_names(all_examples)
write_snapshot_example_files(all_examples, skip_static_and_wysiwyg: skip_static_and_wysiwyg) write_snapshot_example_files(all_examples, skip_static_and_wysiwyg: skip_static_and_wysiwyg)
end end
private private
def read_input_files
[
GLFM_SPEC_TXT_PATH,
GLFM_EXAMPLE_STATUS_YML_PATH
].map do |path|
output("Reading #{path}...")
File.open(path).readlines
end
end
def add_example_names(all_examples) def add_example_names(all_examples)
# NOTE: This method assumes: # NOTE: This method assumes:
# 1. Section 2 is the first section which contains examples # 1. Section 2 is the first section which contains examples
@ -83,7 +79,7 @@ module Glfm
formatted_headers_text = headers.join('__').tr('-', '_').tr(' ', '_').downcase formatted_headers_text = headers.join('__').tr('-', '_').tr(' ', '_').downcase
hierarchy_level = "#{h1_count.to_s.rjust(2, '0')}_#{h2_count.to_s.rjust(2, '0')}" hierarchy_level = "#{h1_count.to_s.rjust(2, '0')}_#{h2_count.to_s.rjust(2, '0')}"
position_within_section = index_within_h2.to_s.rjust(2, '0') position_within_section = index_within_h2.to_s.rjust(3, '0')
name = "#{hierarchy_level}__#{formatted_headers_text}__#{position_within_section}" name = "#{hierarchy_level}__#{formatted_headers_text}__#{position_within_section}"
converted_name = name.tr('(', '').tr(')', '') # remove any parens from the name converted_name = name.tr('(', '').tr(')', '') # remove any parens from the name
example[:name] = converted_name example[:name] = converted_name
@ -91,6 +87,10 @@ module Glfm
end end
def write_snapshot_example_files(all_examples, skip_static_and_wysiwyg:) def write_snapshot_example_files(all_examples, skip_static_and_wysiwyg:)
output("Reading #{GLFM_EXAMPLE_STATUS_YML_PATH}...")
glfm_examples_statuses = YAML.safe_load(File.open(GLFM_EXAMPLE_STATUS_YML_PATH))
validate_glfm_example_status_yml(glfm_examples_statuses)
write_examples_index_yml(all_examples) write_examples_index_yml(all_examples)
write_markdown_yml(all_examples) write_markdown_yml(all_examples)
@ -104,9 +104,20 @@ module Glfm
static_html_hash = generate_static_html(markdown_yml_tempfile_path) static_html_hash = generate_static_html(markdown_yml_tempfile_path)
wysiwyg_html_and_json_hash = generate_wysiwyg_html_and_json(markdown_yml_tempfile_path) wysiwyg_html_and_json_hash = generate_wysiwyg_html_and_json(markdown_yml_tempfile_path)
write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash) write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash, glfm_examples_statuses)
write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash) write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash, glfm_examples_statuses)
end
def validate_glfm_example_status_yml(glfm_examples_statuses)
glfm_examples_statuses.each do |example_name, statuses|
next unless statuses &&
statuses['skip_update_example_snapshots'] &&
statuses.any? { |key, value| key.include?('skip_update_example_snapshot_') && !!value }
raise "Error: '#{example_name}' must not have any 'skip_update_example_snapshot_*' values specified " \
"if 'skip_update_example_snapshots' is truthy"
end
end end
def write_examples_index_yml(all_examples) def write_examples_index_yml(all_examples)
@ -186,27 +197,77 @@ module Glfm
YAML.load_file(wysiwyg_html_and_json_tempfile_path) YAML.load_file(wysiwyg_html_and_json_tempfile_path)
end end
def write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash) def write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash, glfm_examples_statuses)
generate_and_write_for_all_examples(all_examples, ES_HTML_YML_PATH) do |example, hash| generate_and_write_for_all_examples(
hash[example.fetch(:name)] = { all_examples, ES_HTML_YML_PATH, glfm_examples_statuses
) do |example, hash, existing_hash|
name = example.fetch(:name)
example_statuses = glfm_examples_statuses[name] || {}
static = if example_statuses['skip_update_example_snapshot_html_static']
existing_hash.dig(name, 'static')
else
static_html_hash[name]
end
wysiwyg = if example_statuses['skip_update_example_snapshot_html_wysiwyg']
existing_hash.dig(name, 'wysiwyg')
else
wysiwyg_html_and_json_hash.dig(name, 'html')
end
hash[name] = {
'canonical' => example.fetch(:html), 'canonical' => example.fetch(:html),
'static' => static_html_hash.fetch(example.fetch(:name)), 'static' => static,
'wysiwyg' => wysiwyg_html_and_json_hash.fetch(example.fetch(:name)).fetch('html') 'wysiwyg' => wysiwyg
} }.compact # Do not assign nil values
end end
end end
def write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash) def write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash, glfm_examples_statuses)
generate_and_write_for_all_examples(all_examples, ES_PROSEMIRROR_JSON_YML_PATH) do |example, hash| generate_and_write_for_all_examples(
hash[example.fetch(:name)] = wysiwyg_html_and_json_hash.fetch(example.fetch(:name)).fetch('json') all_examples, ES_PROSEMIRROR_JSON_YML_PATH, glfm_examples_statuses
) do |example, hash, existing_hash|
name = example.fetch(:name)
json = if glfm_examples_statuses.dig(name, 'skip_update_example_snapshot_prosemirror_json')
existing_hash.dig(name)
else
wysiwyg_html_and_json_hash.dig(name, 'json')
end
# Do not assign nil values
hash[name] = json if json
end end
end end
def generate_and_write_for_all_examples(all_examples, output_file_path, literal_scalars: true, &generator_block) def generate_and_write_for_all_examples(
output("Writing #{output_file_path}...") all_examples, output_file_path, glfm_examples_statuses = {}, literal_scalars: true
generated_examples_hash = all_examples.each_with_object({}, &generator_block) )
preserve_existing = !glfm_examples_statuses.empty?
output("#{preserve_existing ? 'Creating/Updating' : 'Creating/Overwriting'} #{output_file_path}...")
existing_hash = preserve_existing ? YAML.safe_load(File.open(output_file_path)) : {}
yaml_string = dump_yaml_with_formatting(generated_examples_hash, literal_scalars: literal_scalars) output_hash = all_examples.each_with_object({}) do |example, hash|
name = example.fetch(:name)
if (reason = glfm_examples_statuses.dig(name, 'skip_update_example_snapshots'))
# Output the reason for skipping the example, but only once, not multiple times for each file
output("Skipping '#{name}'. Reason: #{reason}") unless glfm_examples_statuses.dig(name, :already_printed)
# We just store the `:already_printed` flag in the hash entry itself. Then we
# don't need an instance variable to keep the state, and this can remain a pure function ;)
glfm_examples_statuses[name][:already_printed] = true
# Copy over the existing example only if it exists and preserve_existing is true, otherwise omit this example
# noinspection RubyScope
hash[name] = existing_hash[name] if existing_hash[name]
next
end
yield(example, hash, existing_hash)
end
yaml_string = dump_yaml_with_formatting(output_hash, literal_scalars: literal_scalars)
write_file(output_file_path, yaml_string) write_file(output_file_path, yaml_string)
end end

View File

@ -18,7 +18,6 @@ RSpec.describe GroupsController, factory_default: :keep do
let_it_be(:guest) { group.add_guest(create(:user)).user } let_it_be(:guest) { group.add_guest(create(:user)).user }
before do before do
stub_feature_flags(vue_issues_list: true)
enable_admin_mode!(admin_with_admin_mode) enable_admin_mode!(admin_with_admin_mode)
end end

View File

@ -12,10 +12,6 @@ RSpec.describe Projects::IssuesController do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:spam_action_response_fields) { { 'stub_spam_action_response_fields' => true } } let(:spam_action_response_fields) { { 'stub_spam_action_response_fields' => true } }
before do
stub_feature_flags(vue_issues_list: true)
end
describe "GET #index" do describe "GET #index" do
context 'external issue tracker' do context 'external issue tracker' do
before do before do

View File

@ -43,18 +43,6 @@ RSpec.describe Repositories::GitHttpController do
post :git_upload_pack, params: params post :git_upload_pack, params: params
end end
context 'on a read-only instance' do
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
it 'does not update project statistics' do
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
send_request
end
end
it 'updates project statistics sync for projects' do it 'updates project statistics sync for projects' do
stub_feature_flags(disable_git_http_fetch_writes: false) stub_feature_flags(disable_git_http_fetch_writes: false)
@ -83,7 +71,6 @@ RSpec.describe Repositories::GitHttpController do
it 'does not increment statistics' do it 'does not increment statistics' do
expect(Projects::FetchStatisticsIncrementService).not_to receive(:new) expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
send_request send_request
end end

View File

@ -7,8 +7,6 @@ RSpec.describe 'Group empty states' do
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user } let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
before do before do
stub_feature_flags(vue_issues_list: true)
sign_in(user) sign_in(user)
end end

View File

@ -11,10 +11,6 @@ RSpec.describe 'Group issues page' do
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) } let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
let(:path) { issues_group_path(group) } let(:path) { issues_group_path(group) }
before do
stub_feature_flags(vue_issues_list: true)
end
context 'with shared examples', :js do context 'with shared examples', :js do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")} let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
@ -140,8 +136,6 @@ RSpec.describe 'Group issues page' do
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) } let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
before do before do
stub_feature_flags(vue_issues_list: false)
sign_in(user_in_group) sign_in(user_in_group)
end end
@ -164,45 +158,36 @@ RSpec.describe 'Group issues page' do
end end
it 'issues should be draggable and persist order' do it 'issues should be draggable and persist order' do
visit issues_group_path(group, sort: 'relative_position') visit issues_group_path(group)
select_manual_sort
wait_for_requests drag_to(selector: '.manual-ordering', from_index: 0, to_index: 2)
drag_to(selector: '.manual-ordering', expect_issue_order
from_index: 0,
to_index: 2)
wait_for_requests visit issues_group_path(group)
check_issue_order expect_issue_order
visit issues_group_path(group, sort: 'relative_position')
check_issue_order
end end
it 'issues should not be draggable when user is not logged in' do it 'issues should not be draggable when user is not logged in' do
sign_out(user_in_group) sign_out(user_in_group)
visit issues_group_path(group, sort: 'relative_position')
wait_for_requests wait_for_requests
visit issues_group_path(group)
select_manual_sort
drag_to(selector: '.manual-ordering', drag_to(selector: '.manual-ordering', from_index: 0, to_index: 2)
from_index: 0,
to_index: 2)
wait_for_requests expect(page).to have_text 'An error occurred while reordering issues.'
# Issue order should remain the same
page.within('.manual-ordering') do
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #1')
expect(find('.issue:nth-child(2) .title')).to have_content('Issue #2')
expect(find('.issue:nth-child(3) .title')).to have_content('Issue #3')
end
end end
def check_issue_order def select_manual_sort
click_button 'Created date'
click_button 'Manual'
wait_for_requests
end
def expect_issue_order
expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2') expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2')
expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3') expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3')
expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1') expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')

View File

@ -2,23 +2,50 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Groups > User sees users dropdowns in issuables list' do RSpec.describe 'Groups > User sees users dropdowns in issuables list', :js do
let(:entity) { create(:group) } include FilteredSearchHelpers
let(:group) { create(:group) }
let(:user_in_dropdown) { create(:user) } let(:user_in_dropdown) { create(:user) }
let!(:user_not_in_dropdown) { create(:user) } let!(:user_not_in_dropdown) { create(:user) }
let!(:project) { create(:project, group: entity) } let!(:project) { create(:project, group: group) }
before do before do
entity.add_developer(user_in_dropdown) group.add_developer(user_in_dropdown)
sign_in(user_in_dropdown)
end end
it_behaves_like 'issuable user dropdown behaviors' do describe 'issues' do
let(:issuable) { create(:issue, project: project) } let!(:issuable) { create(:issue, project: project) }
let(:issuables_path) { issues_group_path(entity) }
%w[Author Assignee].each do |dropdown|
describe "#{dropdown} dropdown" do
it 'only includes members of the project/group' do
visit issues_group_path(group)
select_tokens dropdown, '=', submit: false
expect_suggestion(user_in_dropdown.name)
expect_no_suggestion(user_not_in_dropdown.name)
end
end
end
end end
it_behaves_like 'issuable user dropdown behaviors' do describe 'merge requests' do
let(:issuable) { create(:merge_request, source_project: project) } let!(:issuable) { create(:merge_request, source_project: project) }
let(:issuables_path) { merge_requests_group_path(entity) }
%w[author assignee].each do |dropdown|
describe "#{dropdown} dropdown" do
it 'only includes members of the project/group' do
visit merge_requests_group_path(group)
filtered_search.set("#{dropdown}:=")
expect(find("#js-dropdown-#{dropdown} .filter-dropdown")).to have_content(user_in_dropdown.name)
expect(find("#js-dropdown-#{dropdown} .filter-dropdown")).not_to have_content(user_not_in_dropdown.name)
end
end
end
end end
end end

View File

@ -9,8 +9,6 @@ RSpec.describe 'issuable list', :js do
issuable_types = [:issue, :merge_request] issuable_types = [:issue, :merge_request]
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_user(user, :developer) project.add_user(user, :developer)
sign_in(user) sign_in(user)
issuable_types.each { |type| create_issuables(type) } issuable_types.each { |type| create_issuables(type) }

View File

@ -15,10 +15,6 @@ RSpec.describe 'Issue rebalancing' do
group.add_developer(user) group.add_developer(user)
end end
before do
stub_feature_flags(vue_issues_list: true)
end
context 'when issue rebalancing is in progress' do context 'when issue rebalancing is in progress' do
before do before do
sign_in(user) sign_in(user)

View File

@ -9,10 +9,6 @@ RSpec.describe 'Dropdown assignee', :js do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do
stub_feature_flags(vue_issues_list: true)
end
describe 'behavior' do describe 'behavior' do
before do before do
project.add_maintainer(user) project.add_maintainer(user)

View File

@ -10,8 +10,6 @@ RSpec.describe 'Dropdown author', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -10,8 +10,6 @@ RSpec.describe 'Dropdown base', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -11,8 +11,6 @@ RSpec.describe 'Dropdown emoji', :js do
let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) } let_it_be(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup') create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown') create_list(:award_emoji, 1, user: user, name: 'thumbsdown')

View File

@ -10,8 +10,6 @@ RSpec.describe 'Dropdown hint', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
end end

View File

@ -11,8 +11,6 @@ RSpec.describe 'Dropdown label', :js do
let_it_be(:label) { create(:label, project: project, title: 'bug-label') } let_it_be(:label) { create(:label, project: project, title: 'bug-label') }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -12,8 +12,6 @@ RSpec.describe 'Dropdown milestone', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -12,8 +12,6 @@ RSpec.describe 'Dropdown release', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -19,7 +19,6 @@ RSpec.describe 'Filter issues', :js do
end end
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
create(:issue, project: project, author: user2, title: "Bug report 1") create(:issue, project: project, author: user2, title: "Bug report 1")

View File

@ -13,8 +13,6 @@ RSpec.describe 'Recent searches', :js do
let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" } let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" }
before do before do
stub_feature_flags(vue_issues_list: true)
# Visit any fast-loading page so we can clear local storage without a DOM exception # Visit any fast-loading page so we can clear local storage without a DOM exception
visit '/404' visit '/404'
remove_recent_searches remove_recent_searches

View File

@ -10,7 +10,6 @@ RSpec.describe 'Search bar', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)

View File

@ -15,7 +15,6 @@ RSpec.describe 'Visual tokens', :js do
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_user(user, :maintainer) project.add_user(user, :maintainer)
project.add_user(user_rock, :maintainer) project.add_user(user_rock, :maintainer)
sign_in(user) sign_in(user)

View File

@ -13,10 +13,6 @@ RSpec.describe 'Project Issues RSS', :js do
group.add_developer(user) group.add_developer(user)
end end
before do
stub_feature_flags(vue_issues_list: true)
end
context 'when signed in' do context 'when signed in' do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }

View File

@ -15,10 +15,6 @@ RSpec.describe 'Issues > Labels bulk assignment' do
let(:issue_1_selector) { "#issuable_#{issue1.id}" } let(:issue_1_selector) { "#issuable_#{issue1.id}" }
let(:issue_2_selector) { "#issuable_#{issue2.id}" } let(:issue_2_selector) { "#issuable_#{issue2.id}" }
before do
stub_feature_flags(vue_issues_list: true)
end
context 'as an allowed user', :js do context 'as an allowed user', :js do
before do before do
project.add_maintainer(user) project.add_maintainer(user)

View File

@ -107,10 +107,6 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
describe 'select all issues' do describe 'select all issues' do
let!(:issue_2) { create(:issue, project: project) } let!(:issue_2) { create(:issue, project: project) }
before do
stub_feature_flags(vue_issues_list: true)
end
it 'after selecting all issues, unchecking one issue only unselects that one issue' do it 'after selecting all issues, unchecking one issue only unselects that one issue' do
visit project_issues_path(project) visit project_issues_path(project)

View File

@ -8,10 +8,6 @@ RSpec.describe "User creates issue" do
let_it_be(:project) { create(:project_empty_repo, :public) } let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
before do
stub_feature_flags(vue_issues_list: true)
end
context "when unauthenticated" do context "when unauthenticated" do
before do before do
sign_out(:user) sign_out(:user)

View File

@ -7,8 +7,6 @@ RSpec.describe 'User filters issues', :js do
let_it_be(:project) { create(:project_empty_repo, :public) } let_it_be(:project) { create(:project_empty_repo, :public) }
before do before do
stub_feature_flags(vue_issues_list: true)
%w[foobar barbaz].each do |title| %w[foobar barbaz].each do |title|
create(:issue, create(:issue,
author: user, author: user,

View File

@ -8,8 +8,6 @@ RSpec.describe 'New issue breadcrumb' do
let(:user) { project.creator } let(:user) { project.creator }
before do before do
stub_feature_flags(vue_issues_list: true)
sign_in(user) sign_in(user)
visit(new_project_issue_path(project)) visit(new_project_issue_path(project))
end end

View File

@ -16,8 +16,6 @@ RSpec.describe "User sorts issues" do
let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') } let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
before do before do
stub_feature_flags(vue_issues_list: true)
create_list(:award_emoji, 2, :upvote, awardable: issue1) create_list(:award_emoji, 2, :upvote, awardable: issue1)
create_list(:award_emoji, 2, :downvote, awardable: issue2) create_list(:award_emoji, 2, :downvote, awardable: issue2)
create(:award_emoji, :downvote, awardable: issue1) create(:award_emoji, :downvote, awardable: issue1)

View File

@ -17,8 +17,6 @@ RSpec.describe 'Labels Hierarchy', :js do
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') } let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
before do before do
stub_feature_flags(vue_issues_list: true)
grandparent.add_owner(user) grandparent.add_owner(user)
sign_in(user) sign_in(user)

View File

@ -17,8 +17,6 @@ RSpec.describe 'User sorts merge requests', :js do
let_it_be(:project) { create(:project, :public, group: group) } let_it_be(:project) { create(:project, :public, group: group) }
before do before do
stub_feature_flags(vue_issues_list: true)
sign_in(user) sign_in(user)
visit(project_merge_requests_path(project)) visit(project_merge_requests_path(project))

View File

@ -16,8 +16,6 @@ RSpec.describe "User sorts things", :js do
let_it_be(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: current_user) } let_it_be(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: current_user) }
before do before do
stub_feature_flags(vue_issues_list: true)
project.add_developer(current_user) project.add_developer(current_user)
sign_in(current_user) sign_in(current_user)
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,8 @@ const TEST_PROJECT_ID = 'abcproject';
const TEST_MERGE_REQUEST_ID = '9001'; const TEST_MERGE_REQUEST_ID = '9001';
const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ID}`; const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ID}`;
jest.mock('~/lib/utils/poll');
describe('ideStatusBar', () => { describe('ideStatusBar', () => {
let store; let store;
let vm; let vm;

View File

@ -11,7 +11,6 @@ describe('JiraIssuesFields', () => {
const defaultProps = { const defaultProps = {
showJiraVulnerabilitiesIntegration: true, showJiraVulnerabilitiesIntegration: true,
upgradePlanPath: 'https://gitlab.com',
}; };
const createComponent = ({ const createComponent = ({

View File

@ -1,31 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import JiraUpgradeCta from '~/integrations/edit/components/jira_upgrade_cta.vue';
describe('JiraUpgradeCta', () => {
let wrapper;
const contentMessage = 'Upgrade your plan to enable this feature of the Jira Integration.';
const createComponent = (propsData) => {
wrapper = shallowMount(JiraUpgradeCta, {
propsData,
});
};
afterEach(() => {
wrapper.destroy();
});
it('displays the correct message for premium and lower users', () => {
createComponent({ showPremiumMessage: true });
expect(wrapper.text()).toContain('This is a Premium feature');
expect(wrapper.text()).toContain(contentMessage);
});
it('displays the correct message for ultimate and lower users', () => {
createComponent({ showUltimateMessage: true });
expect(wrapper.text()).toContain('This is an Ultimate feature');
expect(wrapper.text()).toContain(contentMessage);
});
});

View File

@ -2,7 +2,11 @@ import { GlButton, GlLink, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import eventHub from '~/invite_members/event_hub'; import eventHub from '~/invite_members/event_hub';
import { TRIGGER_ELEMENT_BUTTON, TRIGGER_ELEMENT_SIDE_NAV } from '~/invite_members/constants'; import {
TRIGGER_ELEMENT_BUTTON,
TRIGGER_ELEMENT_SIDE_NAV,
TRIGGER_DEFAULT_QA_SELECTOR,
} from '~/invite_members/constants';
jest.mock('~/experimentation/experiment_tracking'); jest.mock('~/experimentation/experiment_tracking');
@ -50,12 +54,24 @@ describe.each(triggerItems)('with triggerElement as %s', (triggerItem) => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('displayText', () => { describe('configurable attributes', () => {
it('includes the correct displayText for the button', () => { it('includes the correct displayText for the button', () => {
createComponent(); createComponent();
expect(findButton().text()).toBe(displayText); expect(findButton().text()).toBe(displayText);
}); });
it('uses the default qa selector value', () => {
createComponent();
expect(findButton().attributes('data-qa-selector')).toBe(TRIGGER_DEFAULT_QA_SELECTOR);
});
it('sets the qa selector value', () => {
createComponent({ qaSelector: '_qaSelector_' });
expect(findButton().attributes('data-qa-selector')).toBe('_qaSelector_');
});
}); });
describe('clicking the link', () => { describe('clicking the link', () => {

View File

@ -746,7 +746,7 @@ eos
end end
end end
describe '#work_in_progress?' do describe '#draft?' do
[ [
'squash! ', 'fixup! ', 'squash! ', 'fixup! ',
'draft: ', '[Draft] ', '(draft) ', 'Draft: ' 'draft: ', '[Draft] ', '(draft) ', 'Draft: '
@ -754,21 +754,21 @@ eos
it "detects the '#{draft_prefix}' prefix" do it "detects the '#{draft_prefix}' prefix" do
commit.message = "#{draft_prefix}#{commit.message}" commit.message = "#{draft_prefix}#{commit.message}"
expect(commit).to be_work_in_progress expect(commit).to be_draft
end end
end end
it "does not detect WIP for a commit just saying 'draft'" do it "does not detect a commit just saying 'draft' as draft? == true" do
commit.message = "draft" commit.message = "draft"
expect(commit).not_to be_work_in_progress expect(commit).not_to be_draft
end end
["FIXUP!", "Draft - ", "Wipeout", "WIP: ", "[WIP] ", "wip: "].each do |draft_prefix| ["FIXUP!", "Draft - ", "Wipeout", "WIP: ", "[WIP] ", "wip: "].each do |draft_prefix|
it "doesn't detect '#{draft_prefix}' at the start of the title as a draft" do it "doesn't detect '#{draft_prefix}' at the start of the title as a draft" do
commit.message = "#{draft_prefix} #{commit.message}" commit.message = "#{draft_prefix} #{commit.message}"
expect(commit).not_to be_work_in_progress expect(commit).not_to be_draft
end end
end end
end end

View File

@ -1381,7 +1381,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
end end
end end
describe "#work_in_progress?" do describe "#draft?" do
subject { build_stubbed(:merge_request) } subject { build_stubbed(:merge_request) }
[ [
@ -1390,7 +1390,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
it "detects the '#{draft_prefix}' prefix" do it "detects the '#{draft_prefix}' prefix" do
subject.title = "#{draft_prefix}#{subject.title}" subject.title = "#{draft_prefix}#{subject.title}"
expect(subject.work_in_progress?).to eq true expect(subject.draft?).to eq true
end end
end end

View File

@ -308,7 +308,7 @@ RSpec.describe MergeRequestPresenter do
end end
before do before do
allow(resource).to receive(:work_in_progress?).and_return(true) allow(resource).to receive(:draft?).and_return(true)
end end
context 'when merge request enabled and has permission' do context 'when merge request enabled and has permission' do

View File

@ -2,6 +2,31 @@
require 'fast_spec_helper' require 'fast_spec_helper'
require_relative '../../../../scripts/lib/glfm/update_example_snapshots' require_relative '../../../../scripts/lib/glfm/update_example_snapshots'
# IMPORTANT NOTE: See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/
# for details on the implementation and usage of the `update_example_snapshots` script being tested.
# This developers guide contains diagrams and documentation of the script,
# including explanations and examples of all files it reads and writes.
#
# Note that this test is not structured in a traditional way, with multiple examples
# to cover all different scenarios. Instead, the content of the stubbed test fixture
# files are crafted to cover multiple scenarios with in a single example run.
#
# This is because the invocation of the full script is slow, because it executes
# two subshells for processing, one which runs a full Rails environment, and one
# which runs a jest test environment. This results in each full run of the script
# taking between 30-60 seconds. The majority of this is spent loading the Rails environmnent.
#
# However, only the `writing html.yml and prosemirror_json.yml` context is used
# to test these slow sub-processes, and it only contains a single example.
#
# All other tests currently in the file pass the `skip_static_and_wysiwyg: true`
# flag to `#process`, which skips the slow sub-processes. All of these tests
# should run in sub-second time when the Spring pre-loader is used. This allows
# logic which is not directly related to the slow sub-processes to be TDD'd with a
# very rapid feedback cycle.
#
# Also, the textual content of the individual fixture file entries is also crafted to help
# indicate which scenarios which they are covering.
RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
subject { described_class.new } subject { described_class.new }
@ -17,8 +42,10 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
let(:es_markdown_yml_path) { described_class::ES_MARKDOWN_YML_PATH } let(:es_markdown_yml_path) { described_class::ES_MARKDOWN_YML_PATH }
let(:es_markdown_yml_io) { StringIO.new } let(:es_markdown_yml_io) { StringIO.new }
let(:es_html_yml_path) { described_class::ES_HTML_YML_PATH } let(:es_html_yml_path) { described_class::ES_HTML_YML_PATH }
let(:es_html_yml_io_existing) { StringIO.new(es_html_yml_io_existing_contents) }
let(:es_html_yml_io) { StringIO.new } let(:es_html_yml_io) { StringIO.new }
let(:es_prosemirror_json_yml_path) { described_class::ES_PROSEMIRROR_JSON_YML_PATH } let(:es_prosemirror_json_yml_path) { described_class::ES_PROSEMIRROR_JSON_YML_PATH }
let(:es_prosemirror_json_yml_io_existing) { StringIO.new(es_prosemirror_json_yml_io_existing_contents) }
let(:es_prosemirror_json_yml_io) { StringIO.new } let(:es_prosemirror_json_yml_io) { StringIO.new }
# Internal tempfiles # Internal tempfiles
@ -44,7 +71,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
<p><strong>bold</strong></p> <p><strong>bold</strong></p>
```````````````````````````````` ````````````````````````````````
```````````````````````````````` example strikethrough ```````````````````````````````` example strong
__bold with more text__ __bold with more text__
. .
<p><strong>bold with more text</strong></p> <p><strong>bold with more text</strong></p>
@ -90,6 +117,24 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
</strong></p> </strong></p>
```````````````````````````````` ````````````````````````````````
# Third GitLab-Specific Section with skipped Examples
## Strong but skipped
```````````````````````````````` example gitlab strong
**this example will be skipped**
.
<p><strong>this example will be skipped</strong></p>
````````````````````````````````
## Strong but manually modified and skipped
```````````````````````````````` example gitlab strong
**This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved**
.
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
````````````````````````````````
<!-- END TESTS --> <!-- END TESTS -->
# Appendix # Appendix
@ -99,25 +144,90 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
end end
let(:glfm_example_status_yml_contents) do let(:glfm_example_status_yml_contents) do
# language=YAML
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS <<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
--- ---
- 07_01_first_gitlab_specific_section_with_examples_strong_but_with_two_asterisks: 02_01__inlines__strong__001:
# The skip_update_example_snapshots key is present, but false, so this example is not skipped
skip_update_example_snapshots: false skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false 02_01__inlines__strong__002:
skip_running_snapshot_wysiwyg_html_tests: false # It is OK to have an empty (nil) value for an example statuses entry, it means they will all be false.
skip_running_snapshot_prosemirror_json_tests: false 05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
skip_running_conformance_static_tests: false # Always skip this example
skip_running_conformance_wysiwyg_tests: false skip_update_example_snapshots: 'skipping this example because it is very bad'
- 07_02_first_gitlab_specific_section_with_examples_strong_but_with_html: 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
skip_update_example_snapshots: false # Always skip this example, but preserve the existing manual modifications
skip_running_snapshot_static_html_tests: false skip_update_example_snapshots: 'skipping this example because we have manually modified it'
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false
GLFM_EXAMPLE_STATUS_YML_CONTENTS GLFM_EXAMPLE_STATUS_YML_CONTENTS
end end
let(:es_html_yml_io_existing_contents) do
# language=YAML
<<~ES_HTML_YML_IO_EXISTING_CONTENTS
---
00_00__obsolete_entry_to_be_deleted__001:
canonical: |
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
static: |-
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
wysiwyg: |-
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
02_01__inlines__strong__001:
canonical: |
This entry is existing, but not skipped, so it will be overwritten.
static: |-
This entry is existing, but not skipped, so it will be overwritten.
wysiwyg: |-
This entry is existing, but not skipped, so it will be overwritten.
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
canonical: |
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
static: |-
<p>This is the manually modified static HTML which will be preserved</p>
wysiwyg: |-
<p>This is the manually modified WYSIWYG HTML which will be preserved</p>
ES_HTML_YML_IO_EXISTING_CONTENTS
end
let(:es_prosemirror_json_yml_io_existing_contents) do
# language=YAML
<<~ES_PROSEMIRROR_JSON_YML_IO_EXISTING_CONTENTS
---
00_00__obsolete_entry_to_be_deleted__001:
{
"obsolete": "This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted."
}
02_01__inlines__strong__001: |-
{
"existing": "This entry is existing, but not skipped, so it will be overwritten."
}
# 02_01__inlines__strong__002: is omitted from the existing file and skipped, to test that scenario.
02_02__inlines__strikethrough_extension__001: |-
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "~~Hi~~ Hello, world!"
}
]
}
]
}
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshot_prosemirror_json will be truthy"
}
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshots will be truthy"
}
ES_PROSEMIRROR_JSON_YML_IO_EXISTING_CONTENTS
end
before do before do
# We mock out the URI and local file IO objects with real StringIO, instead of just mock # We mock out the URI and local file IO objects with real StringIO, instead of just mock
# objects. This gives better and more realistic coverage, while still avoiding # objects. This gives better and more realistic coverage, while still avoiding
@ -129,12 +239,14 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# output files # output files
allow(File).to receive(:open).with(es_examples_index_yml_path, 'w') { es_examples_index_yml_io } allow(File).to receive(:open).with(es_examples_index_yml_path, 'w') { es_examples_index_yml_io }
allow(File).to receive(:open).with(es_html_yml_path, 'w') { es_html_yml_io }
allow(File).to receive(:open).with(es_prosemirror_json_yml_path, 'w') { es_prosemirror_json_yml_io }
# output files which are also input files # output files which are also input files
allow(File).to receive(:open).with(es_markdown_yml_path, 'w') { es_markdown_yml_io } allow(File).to receive(:open).with(es_markdown_yml_path, 'w') { es_markdown_yml_io }
allow(File).to receive(:open).with(es_markdown_yml_path) { es_markdown_yml_io } allow(File).to receive(:open).with(es_markdown_yml_path) { es_markdown_yml_io }
allow(File).to receive(:open).with(es_html_yml_path, 'w') { es_html_yml_io }
allow(File).to receive(:open).with(es_html_yml_path) { es_html_yml_io_existing }
allow(File).to receive(:open).with(es_prosemirror_json_yml_path, 'w') { es_prosemirror_json_yml_io }
allow(File).to receive(:open).with(es_prosemirror_json_yml_path) { es_prosemirror_json_yml_io_existing }
# Allow normal opening of Tempfile files created during script execution. # Allow normal opening of Tempfile files created during script execution.
tempfile_basenames = [ tempfile_basenames = [
@ -152,26 +264,71 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
allow(subject).to receive(:output) allow(subject).to receive(:output)
end end
describe 'when skip_update_example_snapshots is truthy' do
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
let(:expected_unskipped_example) do
/05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001/
end
it 'still writes the example to examples_index.yml' do
subject.process(skip_static_and_wysiwyg: true)
expect(es_examples_index_yml_contents).to match(expected_unskipped_example)
end
it 'still writes the example to markdown.yml' do
subject.process(skip_static_and_wysiwyg: true)
expect(es_markdown_yml_contents).to match(expected_unskipped_example)
end
describe 'when any other skip_update_example_* is also truthy' do
let(:glfm_example_status_yml_contents) do
# language=YAML
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
---
02_01__inlines__strong__001:
skip_update_example_snapshots: 'if the skip_update_example_snapshots key is truthy...'
skip_update_example_snapshot_html_static: '...then no other skip_update_example_* keys can be truthy'
GLFM_EXAMPLE_STATUS_YML_CONTENTS
end
it 'raises an error' do
expected_msg = "Error: '02_01__inlines__strong__001' must not have any 'skip_update_example_snapshot_*' " \
"values specified if 'skip_update_example_snapshots' is truthy"
expect { subject.process }.to raise_error(/#{Regexp.escape(expected_msg)}/)
end
end
end
describe 'writing examples_index.yml' do describe 'writing examples_index.yml' do
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) } let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
let(:expected_examples_index_yml_contents) do let(:expected_examples_index_yml_contents) do
# language=YAML
<<~ES_EXAMPLES_INDEX_YML_CONTENTS <<~ES_EXAMPLES_INDEX_YML_CONTENTS
--- ---
02_01__inlines__strong__01: 02_01__inlines__strong__001:
spec_txt_example_position: 1 spec_txt_example_position: 1
source_specification: commonmark source_specification: commonmark
02_01__inlines__strong__02: 02_01__inlines__strong__002:
spec_txt_example_position: 2 spec_txt_example_position: 2
source_specification: github source_specification: github
02_02__inlines__strikethrough_extension__01: 02_02__inlines__strikethrough_extension__001:
spec_txt_example_position: 3 spec_txt_example_position: 3
source_specification: github source_specification: github
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
spec_txt_example_position: 4 spec_txt_example_position: 4
source_specification: gitlab source_specification: gitlab
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01: 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
spec_txt_example_position: 5 spec_txt_example_position: 5
source_specification: gitlab source_specification: gitlab
05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
spec_txt_example_position: 6
source_specification: gitlab
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
spec_txt_example_position: 7
source_specification: gitlab
ES_EXAMPLES_INDEX_YML_CONTENTS ES_EXAMPLES_INDEX_YML_CONTENTS
end end
@ -185,20 +342,25 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
describe 'writing markdown.yml' do describe 'writing markdown.yml' do
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) } let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
let(:expected_markdown_yml_contents) do let(:expected_markdown_yml_contents) do
# language=YAML
<<~ES_MARKDOWN_YML_CONTENTS <<~ES_MARKDOWN_YML_CONTENTS
--- ---
02_01__inlines__strong__01: | 02_01__inlines__strong__001: |
__bold__ __bold__
02_01__inlines__strong__02: | 02_01__inlines__strong__002: |
__bold with more text__ __bold with more text__
02_02__inlines__strikethrough_extension__01: | 02_02__inlines__strikethrough_extension__001: |
~~Hi~~ Hello, world! ~~Hi~~ Hello, world!
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: | 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |
**bold** **bold**
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01: | 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |
<strong> <strong>
bold bold
</strong> </strong>
05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001: |
**this example will be skipped**
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |
**This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved**
ES_MARKDOWN_YML_CONTENTS ES_MARKDOWN_YML_CONTENTS
end end
@ -213,61 +375,78 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
let(:es_html_yml_contents) { reread_io(es_html_yml_io) } let(:es_html_yml_contents) { reread_io(es_html_yml_io) }
let(:es_prosemirror_json_yml_contents) { reread_io(es_prosemirror_json_yml_io) } let(:es_prosemirror_json_yml_contents) { reread_io(es_prosemirror_json_yml_io) }
# NOTE: This example_status.yml is crafted in conjunction with expected_html_yml_contents
# to test the behavior of the `skip_update_*` flags
let(:glfm_example_status_yml_contents) do let(:glfm_example_status_yml_contents) do
# language=YAML
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS <<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
--- ---
- 02_01_gitlab_specific_section_with_examples_strong_but_with_two_asterisks: 02_01__inlines__strong__002:
skip_update_example_snapshots: false skip_update_example_snapshot_prosemirror_json: "skipping because JSON isn't cool enough"
skip_running_snapshot_static_html_tests: false 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
skip_running_snapshot_wysiwyg_html_tests: false skip_update_example_snapshot_html_static: "skipping because there's too much static"
skip_running_snapshot_prosemirror_json_tests: false 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
skip_running_conformance_static_tests: false skip_update_example_snapshot_html_wysiwyg: 'skipping because what you see is NOT what you get'
skip_running_conformance_wysiwyg_tests: false skip_update_example_snapshot_prosemirror_json: "skipping because JSON still isn't cool enough"
05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
skip_update_example_snapshots: 'skipping this example because it is very bad'
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
skip_update_example_snapshots: 'skipping this example because we have manually modified it'
GLFM_EXAMPLE_STATUS_YML_CONTENTS GLFM_EXAMPLE_STATUS_YML_CONTENTS
end end
let(:glfm_spec_txt_contents) do
<<~GLFM_SPEC_TXT_CONTENTS
---
title: GitLab Flavored Markdown Spec
...
# Introduction
# GitLab-Specific Section with Examples
## Strong but with two asterisks
```````````````````````````````` example gitlab strong
**bold**
.
<p><strong>bold</strong></p>
````````````````````````````````
<!-- END TESTS -->
# Appendix
Appendix text.
GLFM_SPEC_TXT_CONTENTS
end
let(:expected_html_yml_contents) do let(:expected_html_yml_contents) do
# language=YAML
<<~ES_HTML_YML_CONTENTS <<~ES_HTML_YML_CONTENTS
--- ---
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: 02_01__inlines__strong__001:
canonical: | canonical: |
<p><strong>bold</strong></p> <p><strong>bold</strong></p>
static: |- static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p> <p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
wysiwyg: |- wysiwyg: |-
<p><strong>bold</strong></p> <p><strong>bold</strong></p>
02_01__inlines__strong__002:
canonical: |
<p><strong>bold with more text</strong></p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto"><strong>bold with more text</strong></p>
wysiwyg: |-
<p><strong>bold with more text</strong></p>
02_02__inlines__strikethrough_extension__001:
canonical: |
<p><del>Hi</del> Hello, world!</p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><del>Hi</del> Hello, world!</p>
wysiwyg: |-
<p>~~Hi~~ Hello, world!</p>
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
canonical: |
<p><strong>bold</strong></p>
wysiwyg: |-
<p><strong>bold</strong></p>
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
canonical: |
<p><strong>
bold
</strong></p>
static: |-
<strong>&#x000A;bold&#x000A;</strong>
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
canonical: |
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
static: |-
<p>This is the manually modified static HTML which will be preserved</p>
wysiwyg: |-
<p>This is the manually modified WYSIWYG HTML which will be preserved</p>
ES_HTML_YML_CONTENTS ES_HTML_YML_CONTENTS
end end
let(:expected_prosemirror_json_contents) do let(:expected_prosemirror_json_contents) do
# language=YAML
<<~ES_PROSEMIRROR_JSON_YML_CONTENTS <<~ES_PROSEMIRROR_JSON_YML_CONTENTS
--- ---
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |- 02_01__inlines__strong__001: |-
{ {
"type": "doc", "type": "doc",
"content": [ "content": [
@ -287,6 +466,49 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
} }
] ]
} }
02_02__inlines__strikethrough_extension__001: |-
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "~~Hi~~ Hello, world!"
}
]
}
]
}
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |-
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"marks": [
{
"type": "bold"
}
],
"text": "bold"
}
]
}
]
}
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshot_prosemirror_json will be truthy"
}
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshots will be truthy"
}
ES_PROSEMIRROR_JSON_YML_CONTENTS ES_PROSEMIRROR_JSON_YML_CONTENTS
end end
@ -304,6 +526,9 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# the rails environment. We could have separate sections, but this would require an extra flag # the rails environment. We could have separate sections, but this would require an extra flag
# to the `process` method to independently skip static vs. WYSIWYG, which is not worth the effort. # to the `process` method to independently skip static vs. WYSIWYG, which is not worth the effort.
it 'writes the correct content' do it 'writes the correct content' do
# expectation that skipping message is only output once per example
expect(subject).to receive(:output).once.with(/reason.*skipping this example because it is very bad/i)
subject.process subject.process
expect(es_html_yml_contents).to eq(expected_html_yml_contents) expect(es_html_yml_contents).to eq(expected_html_yml_contents)

View File

@ -32,7 +32,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
it 'creates an MR' do it 'creates an MR' do
expect(merge_request).to be_valid expect(merge_request).to be_valid
expect(merge_request.work_in_progress?).to be(false) expect(merge_request.draft?).to be(false)
expect(merge_request.title).to eq('Awesome merge_request') expect(merge_request.title).to eq('Awesome merge_request')
expect(merge_request.assignees).to be_empty expect(merge_request.assignees).to be_empty
expect(merge_request.merge_params['force_remove_source_branch']).to eq('1') expect(merge_request.merge_params['force_remove_source_branch']).to eq('1')
@ -74,7 +74,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
end end
it 'sets MR to draft' do it 'sets MR to draft' do
expect(merge_request.work_in_progress?).to be(true) expect(merge_request.draft?).to be(true)
end end
end end
@ -90,7 +90,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
end end
it 'sets MR to draft' do it 'sets MR to draft' do
expect(merge_request.work_in_progress?).to be(true) expect(merge_request.draft?).to be(true)
end end
end end
end end

View File

@ -756,47 +756,48 @@ RSpec.describe MergeRequests::RefreshService do
refresh_service.execute(oldrev, newrev, 'refs/heads/wip') refresh_service.execute(oldrev, newrev, 'refs/heads/wip')
fixup_merge_request.reload fixup_merge_request.reload
expect(fixup_merge_request.work_in_progress?).to eq(true) expect(fixup_merge_request.draft?).to eq(true)
expect(fixup_merge_request.notes.last.note).to match( expect(fixup_merge_request.notes.last.note).to match(
/marked this merge request as \*\*draft\*\* from #{Commit.reference_pattern}/ /marked this merge request as \*\*draft\*\* from #{Commit.reference_pattern}/
) )
end end
it 'references the commit that caused the draft status' do it 'references the commit that caused the draft status' do
wip_merge_request = create(:merge_request, draft_merge_request = create(:merge_request,
source_project: @project, source_project: @project,
source_branch: 'wip', source_branch: 'wip',
target_branch: 'master', target_branch: 'master',
target_project: @project) target_project: @project)
commits = wip_merge_request.commits commits = draft_merge_request.commits
oldrev = commits.last.id oldrev = commits.last.id
newrev = commits.first.id newrev = commits.first.id
wip_commit = wip_merge_request.commits.find(&:work_in_progress?) draft_commit = draft_merge_request.commits.find(&:draft?)
refresh_service.execute(oldrev, newrev, 'refs/heads/wip') refresh_service.execute(oldrev, newrev, 'refs/heads/wip')
expect(wip_merge_request.reload.notes.last.note).to eq( expect(draft_merge_request.reload.notes.last.note).to eq(
"marked this merge request as **draft** from #{wip_commit.id}" "marked this merge request as **draft** from #{draft_commit.id}"
) )
end end
it 'does not mark as draft based on commits that do not belong to an MR' do it 'does not mark as draft based on commits that do not belong to an MR' do
allow(refresh_service).to receive(:find_new_commits) allow(refresh_service).to receive(:find_new_commits)
refresh_service.instance_variable_set("@commits", [ refresh_service.instance_variable_set("@commits", [
double( double(
id: 'aaaaaaa', id: 'aaaaaaa',
sha: 'aaaaaaa', sha: 'aaaaaaa',
short_id: 'aaaaaaa', short_id: 'aaaaaaa',
title: 'Fix issue', title: 'Fix issue',
work_in_progress?: false draft?: false
), ),
double( double(
id: 'bbbbbbb', id: 'bbbbbbb',
sha: 'bbbbbbbb', sha: 'bbbbbbbb',
short_id: 'bbbbbbb', short_id: 'bbbbbbb',
title: 'fixup! Fix issue', title: 'fixup! Fix issue',
work_in_progress?: true, draft?: true,
to_reference: 'bbbbbbb' to_reference: 'bbbbbbb'
) )
]) ])
@ -804,7 +805,7 @@ RSpec.describe MergeRequests::RefreshService do
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs reload_mrs
expect(@merge_request.work_in_progress?).to be_falsey expect(@merge_request.draft?).to be_falsey
end end
end end

View File

@ -359,7 +359,7 @@ RSpec.describe Notes::CreateService do
issuable.reload.update!(title: "title") issuable.reload.update!(title: "title")
}, },
expectation: ->(issuable, can_use_quick_action) { expectation: ->(issuable, can_use_quick_action) {
expect(issuable.work_in_progress?).to eq(can_use_quick_action) expect(issuable.draft?).to eq(can_use_quick_action)
} }
), ),
# Remove draft status # Remove draft status
@ -369,7 +369,7 @@ RSpec.describe Notes::CreateService do
issuable.reload.update!(title: "Draft: title") issuable.reload.update!(title: "Draft: title")
}, },
expectation: ->(noteable, can_use_quick_action) { expectation: ->(noteable, can_use_quick_action) {
expect(noteable.work_in_progress?).not_to eq(can_use_quick_action) expect(noteable.draft?).not_to eq(can_use_quick_action)
} }
) )
] ]

View File

@ -308,10 +308,6 @@ RSpec.configure do |config|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/33867 # See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
stub_feature_flags(file_identifier_hash: false) stub_feature_flags(file_identifier_hash: false)
# The following `vue_issues_list` stub can be removed
# once the Vue issues page has feature parity with the current Haml page
stub_feature_flags(vue_issues_list: false)
# Disable `main_branch_over_master` as we migrate # Disable `main_branch_over_master` as we migrate
# from `master` to `main` accross our codebase. # from `master` to `main` accross our codebase.
# It's done in order to preserve the concistency in tests # It's done in order to preserve the concistency in tests

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable user dropdown behaviors' do
include FilteredSearchHelpers
before do
issuable # ensure we have at least one issuable
sign_in(user_in_dropdown)
end
%w[author assignee].each do |dropdown|
describe "#{dropdown} dropdown", :js do
it 'only includes members of the project/group' do
visit issuables_path
filtered_search.set("#{dropdown}:=")
expect(find("#js-dropdown-#{dropdown} .filter-dropdown")).to have_content(user_in_dropdown.name)
expect(find("#js-dropdown-#{dropdown} .filter-dropdown")).not_to have_content(user_not_in_dropdown.name)
end
end
end
end

View File

@ -378,7 +378,6 @@ RSpec.describe 'Every Sidekiq worker' do
'PostReceive' => 3, 'PostReceive' => 3,
'ProcessCommitWorker' => 3, 'ProcessCommitWorker' => 3,
'ProjectCacheWorker' => 3, 'ProjectCacheWorker' => 3,
'ProjectDailyStatisticsWorker' => 3,
'ProjectDestroyWorker' => 3, 'ProjectDestroyWorker' => 3,
'ProjectExportWorker' => false, 'ProjectExportWorker' => false,
'ProjectImportScheduleWorker' => 1, 'ProjectImportScheduleWorker' => 1,

View File

@ -1,35 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ProjectDailyStatisticsWorker, '#perform' do
let(:worker) { described_class.new }
let(:project) { create(:project) }
describe '#perform' do
context 'with a non-existing project' do
it 'does nothing' do
expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
worker.perform(-1)
end
end
context 'with an existing project without a repository' do
it 'does nothing' do
expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
worker.perform(project.id)
end
end
it 'calls daily_statistics_service with the given project' do
project = create(:project, :repository)
expect_next_instance_of(Projects::FetchStatisticsIncrementService, project) do |service|
expect(service).to receive(:execute)
end
worker.perform(project.id)
end
end
end