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"
BUNDLE_WITHOUT: "production:development"
BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet"
BUNDLE_FROZEN: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
GIT_DEPTH: "20"

View File

@ -177,6 +177,8 @@ Dangerfile @gl-quality/eng-prod
[Application Security]
/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/public_url_validator.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:
extends:
- .default-retry
- .review:rules:reviewers-recommender
stage: test
needs: []

View File

@ -1663,6 +1663,12 @@
- <<: *if-merge-request
changes: *danger-patterns
.review:rules:reviewers-recommender:
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request
###############
# 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"
- tar -xf "jh-folder.tar.gz"
- mv "gitlab-${JH_BRANCH}-jh/jh/" ./
- cp Gemfile.lock jh/
- ls -l jh/
artifacts:
expire_in: 2d

View File

@ -1 +1 @@
803b179a6834fbebcc7886083731bb0f4a67c796
b2c8eaa672c9f2dc4b55477a3876f957e2c9a768

View File

@ -354,7 +354,7 @@ export default {
>
<header
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"
>
<div

View File

@ -32,8 +32,11 @@ export default {
...mapState('pipelines', ['latestPipeline']),
},
watch: {
lastCommit() {
this.initPipelinePolling();
lastCommit: {
handler() {
this.initPipelinePolling();
},
immediate: true,
},
},
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,
commentDetail,
projectKey,
upgradePlanPath,
learnMorePath,
aboutPricingUrl,
triggerEvents,
@ -80,7 +79,6 @@ function parseDatasetToProps(data) {
initialEnableJiraVulnerabilities: enableJiraVulnerabilities,
initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype,
initialProjectKey: projectKey,
upgradePlanPath,
},
learnMorePath,
aboutPricingUrl,

View File

@ -2,7 +2,11 @@
import { GlButton, GlLink, GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
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 {
components: { GlButton, GlLink, GlIcon },
@ -46,12 +50,17 @@ export default {
required: false,
default: '',
},
qaSelector: {
type: String,
required: false,
default: TRIGGER_DEFAULT_QA_SELECTOR,
},
},
computed: {
componentAttributes() {
const baseAttributes = {
class: this.classes,
'data-qa-selector': 'invite_members_button',
'data-qa-selector': this.qaSelector,
'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 TRIGGER_ELEMENT_BUTTON = 'button';
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_CELEBRATE_TITLE = s__(
'InviteMembersModal|GitLab is better with colleagues!',

View File

@ -11,6 +11,7 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import api from '~/api';
// Todo: Remove this when fixing issue in input_setter plugin
const InputSetter = { ...ISetter };
@ -149,7 +150,7 @@ export default class CreateMergeRequestDropdown {
});
}
createBranch() {
createBranch(navigateToBranch = true) {
this.isCreatingBranch = true;
return axios
@ -158,7 +159,10 @@ export default class CreateMergeRequestDropdown {
})
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
if (navigateToBranch) {
window.location.href = data.url;
}
})
.catch(() =>
createFlash({
@ -170,23 +174,25 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() {
return new Promise(() => {
this.isCreatingMergeRequest = true;
return this.createBranch().then(() => {
let path = canCreateConfidentialMergeRequest()
? this.createMrPath.replace(
this.projectPath,
confidentialMergeRequestState.selectedProject.pathWithNamespace,
)
: this.createMrPath;
path = mergeUrlParams(
{
'merge_request[target_branch]': this.refInput.value,
'merge_request[source_branch]': this.branchInput.value,
},
path,
);
return this.createBranch(false)
.then(() => api.trackRedisHllUserEvent('i_code_review_user_create_mr_from_issue'))
.then(() => {
let path = canCreateConfidentialMergeRequest()
? this.createMrPath.replace(
this.projectPath,
confidentialMergeRequestState.selectedProject.pathWithNamespace,
)
: this.createMrPath;
path = mergeUrlParams(
{
'merge_request[target_branch]': this.refInput.value,
'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;
background-color: #1f1f1f;
}
h1 {
margin-top: 0;
margin-bottom: 0.25rem;
}
ul {
margin-top: 0;
margin-bottom: 1rem;
@ -105,15 +101,6 @@ button::-moz-focus-inner,
[type="search"] {
outline-offset: -2px;
}
h1 {
margin-bottom: 0.25rem;
font-weight: 600;
line-height: 1.2;
color: #fafafa;
}
h1 {
font-size: 2.1875rem;
}
.list-unstyled {
padding-left: 0;
list-style: none;
@ -547,10 +534,6 @@ html [type="button"],
[role="button"] {
cursor: pointer;
}
h1 {
margin-top: 20px;
margin-bottom: 10px;
}
strong {
font-weight: bold;
}

View File

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

View File

@ -139,7 +139,7 @@ module MergeRequestsHelper
end
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 } })
end

View File

@ -519,9 +519,10 @@ class Commit
#
DRAFT_REGEX = /\A\s*#{Gitlab::Regex.merge_request_draft}|(fixup!|squash!)\s/.freeze
def work_in_progress?
def draft?
!!(title =~ DRAFT_REGEX)
end
alias_method :work_in_progress?, :draft?
def 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 :commits_count
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 :can_be_merged?, as: :can_be_merged
expose :remove_source_branch?, as: :remove_source_branch

View File

@ -54,7 +54,7 @@ module Issuable
def create_draft_note(old_title)
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)
end
end

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
.js-invite-members-trigger{ data: { trigger_source: 'group-side-nav',
icon: 'users',
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 'groups/invite_members_modal', group: group

View File

@ -6,7 +6,7 @@
.container-fluid
.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
%h1.title
.title
%span.gl-sr-only GitLab
= link_to root_path, title: _('Dashboard'), id: 'logo', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
= brand_header_logo

View File

@ -1,7 +1,8 @@
.js-invite-members-trigger{ data: { trigger_source: 'project-side-nav',
icon: 'users',
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 'projects/invite_members_modal', project: project

View File

@ -20,7 +20,7 @@
%li.gl-new-dropdown-item
= 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
= @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
= link_to close_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
.gl-new-dropdown-item-text-wrapper

View File

@ -22,7 +22,7 @@
= _('Title')
%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 } }
= 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,
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
.js-wip-explanation{ style: "display: none;" }
= remove_wip_text

View File

@ -2749,15 +2749,6 @@
:weight: 1
:idempotent: true
: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
:worker_name: ProjectDestroyWorker
: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
- - project_cache
- 1
- - project_daily_statistics
- 1
- - project_destroy
- 1
- - 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
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)
by using the [load-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/devops-for-mobile-apps/load-secure-files)
tool.
@ -30,49 +32,14 @@ Additional features and capabilities are planned.
## 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:
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/projects/:project_id/secure_files" --form "name=myfile.jks" --form "file=@/path/to/file/myfile.jks"
```
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"
}]
```
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. In the **Secure Files** section, select **Manage**.
1. Select **Upload File**.
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.
## 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
versions of HTML can potentially be rendered from the example:
1. **Static HTML**: 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.
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.
- Static HTML.
- WYSIWYG HTML.
- Canonical HTML.
As the rendered static and WYSIWYG HTML from the backend (Ruby) and frontend (JavaScript)
renderers contain extra HTML, their rendered HTML can be converted to canonical HTML
by a [canonicalization](#canonicalization-of-html) process.
#### Static HTML
#### 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:
- 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).
- It is manually updated.
- It controls the status of automatic generation of files based on Markdown examples.
- It allows example snapshot generation, Markdown conformance tests, or
Markdown snapshot tests to be skipped for individual examples. For example, if
they are unimplemented, broken, or cannot be tested for some reason.
- The `skip_update_example_snapshot*` fields control the status of automatic generation of
snapshot example entries based on Markdown examples.
- The `skip_running_*` control allow Markdown conformance tests or
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:
```yaml
07_99_an_example_with_incomplete_wysiwyg_implementation_1:
skip_update_example_snapshots: false
skip_update_example_snapshot_html_static: false
skip_update_example_snapshot_html_wysiwyg: false
skip_running_conformance_static_tests: false
skip_running_conformance_wysiwyg_tests: false
skip_running_snapshot_static_html_tests: false
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
skip_update_example_snapshots: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_static: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_wysiwyg: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_prosemirror_json: 'An explanation of the reason for skipping.'
skip_running_conformance_static_tests: 'An explanation of the reason for skipping.'
skip_running_conformance_wysiwyg_tests: 'An explanation of the reason for skipping.'
skip_running_snapshot_static_html_tests: 'An explanation of the reason for skipping.'
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

View File

@ -22,9 +22,17 @@ it should be restricted on namespace scope.
1. Check using:
```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)
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.
- Adding non-critical indices on high-traffic tables.
- 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
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
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
after the limits change in January, 2021:
| 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 |
| **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 |
| **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 |
| **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 |
| **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 |
| **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** |
| 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 |
| **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 |
| **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 |
| **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 |
| **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 |
| **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** |
| **Pipeline creation** requests (for a given **project, user, and commit**) | | **25** requests per minute |
More details are available on the rate limits for [protected
paths](#protected-paths-throttle) and [raw

View File

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

View File

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

View File

@ -52,7 +52,7 @@ module Gitlab
source: merge_request.source_project.try(:hook_attrs),
target: merge_request.target_project.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,
time_change: merge_request.time_change,
human_total_time_spent: merge_request.human_total_time_spent,

View File

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

View File

@ -7684,6 +7684,9 @@ msgstr ""
msgid "Checkout|Name of company or organization using GitLab"
msgstr ""
msgid "Checkout|Name: %{errors}"
msgstr ""
msgid "Checkout|Need more users? Purchase GitLab for your %{company}."
msgstr ""
@ -21828,18 +21831,9 @@ msgstr ""
msgid "JiraService|This feature requires a Premium plan."
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:"
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."
msgstr ""
@ -40735,9 +40729,6 @@ msgstr ""
msgid "Upgrade offers available!"
msgstr ""
msgid "Upgrade your plan"
msgstr ""
msgid "Upload"
msgstr ""

View File

@ -27,7 +27,7 @@ module QA
element :invite_a_group_button
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
end
end

View File

@ -15,7 +15,7 @@ module QA
element :invite_a_group_button
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
end

View File

@ -13,6 +13,10 @@ require_relative 'parse_examples'
# for details on the implementation and usage of this script. This developers guide
# contains diagrams and documentation of this script,
# 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
class UpdateExampleSnapshots
include Constants
@ -27,28 +31,20 @@ module Glfm
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`
# function the from original CommonMark/GFM `spec_test.py` script.
all_examples = parse_examples(glfm_spec_txt_lines)
add_example_names(all_examples)
write_snapshot_example_files(all_examples, skip_static_and_wysiwyg: skip_static_and_wysiwyg)
end
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)
# NOTE: This method assumes:
# 1. Section 2 is the first section which contains examples
@ -83,7 +79,7 @@ module Glfm
formatted_headers_text = headers.join('__').tr('-', '_').tr(' ', '_').downcase
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}"
converted_name = name.tr('(', '').tr(')', '') # remove any parens from the name
example[:name] = converted_name
@ -91,6 +87,10 @@ module Glfm
end
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_markdown_yml(all_examples)
@ -104,9 +104,20 @@ module Glfm
static_html_hash = generate_static_html(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
def write_examples_index_yml(all_examples)
@ -186,27 +197,77 @@ module Glfm
YAML.load_file(wysiwyg_html_and_json_tempfile_path)
end
def write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash)
generate_and_write_for_all_examples(all_examples, ES_HTML_YML_PATH) do |example, hash|
hash[example.fetch(:name)] = {
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, 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),
'static' => static_html_hash.fetch(example.fetch(:name)),
'wysiwyg' => wysiwyg_html_and_json_hash.fetch(example.fetch(:name)).fetch('html')
}
'static' => static,
'wysiwyg' => wysiwyg
}.compact # Do not assign nil values
end
end
def write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash)
generate_and_write_for_all_examples(all_examples, ES_PROSEMIRROR_JSON_YML_PATH) do |example, hash|
hash[example.fetch(:name)] = wysiwyg_html_and_json_hash.fetch(example.fetch(:name)).fetch('json')
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, 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
def generate_and_write_for_all_examples(all_examples, output_file_path, literal_scalars: true, &generator_block)
output("Writing #{output_file_path}...")
generated_examples_hash = all_examples.each_with_object({}, &generator_block)
def generate_and_write_for_all_examples(
all_examples, output_file_path, glfm_examples_statuses = {}, literal_scalars: true
)
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)
end

View File

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

View File

@ -12,10 +12,6 @@ RSpec.describe Projects::IssuesController do
let(:issue) { create(:issue, project: project) }
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
context 'external issue tracker' do
before do

View File

@ -43,18 +43,6 @@ RSpec.describe Repositories::GitHttpController do
post :git_upload_pack, params: params
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
stub_feature_flags(disable_git_http_fetch_writes: false)
@ -83,7 +71,6 @@ RSpec.describe Repositories::GitHttpController do
it 'does not increment statistics' do
expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
send_request
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 }
before do
stub_feature_flags(vue_issues_list: true)
sign_in(user)
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(:path) { issues_group_path(group) }
before do
stub_feature_flags(vue_issues_list: true)
end
context 'with shared examples', :js do
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) }
before do
stub_feature_flags(vue_issues_list: false)
sign_in(user_in_group)
end
@ -164,45 +158,36 @@ RSpec.describe 'Group issues page' do
end
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',
from_index: 0,
to_index: 2)
expect_issue_order
wait_for_requests
visit issues_group_path(group)
check_issue_order
visit issues_group_path(group, sort: 'relative_position')
check_issue_order
expect_issue_order
end
it 'issues should not be draggable when user is not logged in' do
sign_out(user_in_group)
visit issues_group_path(group, sort: 'relative_position')
wait_for_requests
visit issues_group_path(group)
select_manual_sort
drag_to(selector: '.manual-ordering',
from_index: 0,
to_index: 2)
drag_to(selector: '.manual-ordering', from_index: 0, to_index: 2)
wait_for_requests
# 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
expect(page).to have_text 'An error occurred while reordering issues.'
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(2) .title', text: 'Issue #3')
expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')

View File

@ -2,23 +2,50 @@
require 'spec_helper'
RSpec.describe 'Groups > User sees users dropdowns in issuables list' do
let(:entity) { create(:group) }
RSpec.describe 'Groups > User sees users dropdowns in issuables list', :js do
include FilteredSearchHelpers
let(:group) { create(:group) }
let(:user_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
entity.add_developer(user_in_dropdown)
group.add_developer(user_in_dropdown)
sign_in(user_in_dropdown)
end
it_behaves_like 'issuable user dropdown behaviors' do
let(:issuable) { create(:issue, project: project) }
let(:issuables_path) { issues_group_path(entity) }
describe 'issues' do
let!(:issuable) { create(:issue, project: project) }
%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
it_behaves_like 'issuable user dropdown behaviors' do
let(:issuable) { create(:merge_request, source_project: project) }
let(:issuables_path) { merge_requests_group_path(entity) }
describe 'merge requests' do
let!(:issuable) { create(:merge_request, source_project: project) }
%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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,8 +10,6 @@ RSpec.describe 'Dropdown base', :js do
let_it_be(:issue) { create(:issue, project: project) }
before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(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) }
before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
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) }
before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
end

View File

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

View File

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

View File

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

View File

@ -19,7 +19,6 @@ RSpec.describe 'Filter issues', :js do
end
before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
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" }
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 '/404'
remove_recent_searches

View File

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

View File

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

View File

@ -13,10 +13,6 @@ RSpec.describe 'Project Issues RSS', :js do
group.add_developer(user)
end
before do
stub_feature_flags(vue_issues_list: true)
end
context 'when signed in' do
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_2_selector) { "#issuable_#{issue2.id}" }
before do
stub_feature_flags(vue_issues_list: true)
end
context 'as an allowed user', :js do
before do
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
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
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(:user) { create(:user) }
before do
stub_feature_flags(vue_issues_list: true)
end
context "when unauthenticated" do
before do
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) }
before do
stub_feature_flags(vue_issues_list: true)
%w[foobar barbaz].each do |title|
create(:issue,
author: user,

View File

@ -8,8 +8,6 @@ RSpec.describe 'New issue breadcrumb' do
let(:user) { project.creator }
before do
stub_feature_flags(vue_issues_list: true)
sign_in(user)
visit(new_project_issue_path(project))
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') }
before do
stub_feature_flags(vue_issues_list: true)
create_list(:award_emoji, 2, :upvote, awardable: issue1)
create_list(:award_emoji, 2, :downvote, awardable: issue2)
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') }
before do
stub_feature_flags(vue_issues_list: true)
grandparent.add_owner(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) }
before do
stub_feature_flags(vue_issues_list: true)
sign_in(user)
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) }
before do
stub_feature_flags(vue_issues_list: true)
project.add_developer(current_user)
sign_in(current_user)
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_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ID}`;
jest.mock('~/lib/utils/poll');
describe('ideStatusBar', () => {
let store;
let vm;

View File

@ -11,7 +11,6 @@ describe('JiraIssuesFields', () => {
const defaultProps = {
showJiraVulnerabilitiesIntegration: true,
upgradePlanPath: 'https://gitlab.com',
};
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 InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
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');
@ -50,12 +54,24 @@ describe.each(triggerItems)('with triggerElement as %s', (triggerItem) => {
wrapper.destroy();
});
describe('displayText', () => {
describe('configurable attributes', () => {
it('includes the correct displayText for the button', () => {
createComponent();
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', () => {

View File

@ -746,7 +746,7 @@ eos
end
end
describe '#work_in_progress?' do
describe '#draft?' do
[
'squash! ', 'fixup! ',
'draft: ', '[Draft] ', '(draft) ', 'Draft: '
@ -754,21 +754,21 @@ eos
it "detects the '#{draft_prefix}' prefix" do
commit.message = "#{draft_prefix}#{commit.message}"
expect(commit).to be_work_in_progress
expect(commit).to be_draft
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"
expect(commit).not_to be_work_in_progress
expect(commit).not_to be_draft
end
["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
commit.message = "#{draft_prefix} #{commit.message}"
expect(commit).not_to be_work_in_progress
expect(commit).not_to be_draft
end
end
end

View File

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

View File

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

View File

@ -2,6 +2,31 @@
require 'fast_spec_helper'
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
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_io) { StringIO.new }
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_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 }
# Internal tempfiles
@ -44,7 +71,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
<p><strong>bold</strong></p>
````````````````````````````````
```````````````````````````````` example strikethrough
```````````````````````````````` example strong
__bold with more text__
.
<p><strong>bold with more text</strong></p>
@ -90,6 +117,24 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
</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 -->
# Appendix
@ -99,25 +144,90 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
end
let(:glfm_example_status_yml_contents) do
# language=YAML
<<~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_running_snapshot_static_html_tests: false
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
- 07_02_first_gitlab_specific_section_with_examples_strong_but_with_html:
skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false
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
02_01__inlines__strong__002:
# It is OK to have an empty (nil) value for an example statuses entry, it means they will all be false.
05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
# Always skip this example
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:
# Always skip this example, but preserve the existing manual modifications
skip_update_example_snapshots: 'skipping this example because we have manually modified it'
GLFM_EXAMPLE_STATUS_YML_CONTENTS
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
# 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
@ -129,12 +239,14 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# 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_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
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_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.
tempfile_basenames = [
@ -152,26 +264,71 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
allow(subject).to receive(:output)
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
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
let(:expected_examples_index_yml_contents) do
# language=YAML
<<~ES_EXAMPLES_INDEX_YML_CONTENTS
---
02_01__inlines__strong__01:
02_01__inlines__strong__001:
spec_txt_example_position: 1
source_specification: commonmark
02_01__inlines__strong__02:
02_01__inlines__strong__002:
spec_txt_example_position: 2
source_specification: github
02_02__inlines__strikethrough_extension__01:
02_02__inlines__strikethrough_extension__001:
spec_txt_example_position: 3
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
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
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
end
@ -185,20 +342,25 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
describe 'writing markdown.yml' do
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
let(:expected_markdown_yml_contents) do
# language=YAML
<<~ES_MARKDOWN_YML_CONTENTS
---
02_01__inlines__strong__01: |
02_01__inlines__strong__001: |
__bold__
02_01__inlines__strong__02: |
02_01__inlines__strong__002: |
__bold with more text__
02_02__inlines__strikethrough_extension__01: |
02_02__inlines__strikethrough_extension__001: |
~~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**
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>
bold
</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
end
@ -213,61 +375,78 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
let(:es_html_yml_contents) { reread_io(es_html_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
# language=YAML
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
---
- 02_01_gitlab_specific_section_with_examples_strong_but_with_two_asterisks:
skip_update_example_snapshots: false
skip_running_snapshot_static_html_tests: false
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
02_01__inlines__strong__002:
skip_update_example_snapshot_prosemirror_json: "skipping because JSON isn't cool enough"
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
skip_update_example_snapshot_html_static: "skipping because there's too much static"
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
skip_update_example_snapshot_html_wysiwyg: 'skipping because what you see is NOT what you get'
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
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
# language=YAML
<<~ES_HTML_YML_CONTENTS
---
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
02_01__inlines__strong__001:
canonical: |
<p><strong>bold</strong></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
wysiwyg: |-
<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
end
let(:expected_prosemirror_json_contents) do
# language=YAML
<<~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",
"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
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
# to the `process` method to independently skip static vs. WYSIWYG, which is not worth the effort.
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
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
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.assignees).to be_empty
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
it 'sets MR to draft' do
expect(merge_request.work_in_progress?).to be(true)
expect(merge_request.draft?).to be(true)
end
end
@ -90,7 +90,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
end
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

View File

@ -756,47 +756,48 @@ RSpec.describe MergeRequests::RefreshService do
refresh_service.execute(oldrev, newrev, 'refs/heads/wip')
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(
/marked this merge request as \*\*draft\*\* from #{Commit.reference_pattern}/
)
end
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_branch: 'wip',
target_branch: 'master',
target_project: @project)
commits = wip_merge_request.commits
commits = draft_merge_request.commits
oldrev = commits.last.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')
expect(wip_merge_request.reload.notes.last.note).to eq(
"marked this merge request as **draft** from #{wip_commit.id}"
expect(draft_merge_request.reload.notes.last.note).to eq(
"marked this merge request as **draft** from #{draft_commit.id}"
)
end
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)
refresh_service.instance_variable_set("@commits", [
double(
id: 'aaaaaaa',
sha: 'aaaaaaa',
short_id: 'aaaaaaa',
title: 'Fix issue',
work_in_progress?: false
draft?: false
),
double(
id: 'bbbbbbb',
sha: 'bbbbbbbb',
short_id: 'bbbbbbb',
title: 'fixup! Fix issue',
work_in_progress?: true,
draft?: true,
to_reference: 'bbbbbbb'
)
])
@ -804,7 +805,7 @@ RSpec.describe MergeRequests::RefreshService do
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs
expect(@merge_request.work_in_progress?).to be_falsey
expect(@merge_request.draft?).to be_falsey
end
end

View File

@ -359,7 +359,7 @@ RSpec.describe Notes::CreateService do
issuable.reload.update!(title: "title")
},
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
@ -369,7 +369,7 @@ RSpec.describe Notes::CreateService do
issuable.reload.update!(title: "Draft: title")
},
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
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
# from `master` to `main` accross our codebase.
# 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,
'ProcessCommitWorker' => 3,
'ProjectCacheWorker' => 3,
'ProjectDailyStatisticsWorker' => 3,
'ProjectDestroyWorker' => 3,
'ProjectExportWorker' => false,
'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