Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
20c68317f8
commit
fbb529e46c
37 changed files with 1641 additions and 2014 deletions
|
@ -5,5 +5,4 @@ Gitlab/DelegatePredicateMethods:
|
|||
- app/models/concerns/integrations/base_data_fields.rb
|
||||
- app/models/project.rb
|
||||
- ee/app/models/concerns/ee/ci/metadatable.rb
|
||||
- ee/app/models/license.rb
|
||||
- lib/gitlab/ci/trace/stream.rb
|
||||
|
|
|
@ -149,7 +149,7 @@ export default class SourceEditor {
|
|||
});
|
||||
|
||||
this.instances.push(instance);
|
||||
el.dispatchEvent(new CustomEvent(EDITOR_READY_EVENT, { instance }));
|
||||
el.dispatchEvent(new CustomEvent(EDITOR_READY_EVENT, { detail: { instance } }));
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
|
||||
|
||||
const defaultTimezone = { name: 'UTC', offset: 0 };
|
||||
const defaultTimezone = { identifier: 'Etc/UTC', name: 'UTC', offset: 0 };
|
||||
const defaults = {
|
||||
$inputEl: null,
|
||||
$dropdownEl: null,
|
||||
|
@ -70,7 +70,7 @@ export default class TimezoneDropdown {
|
|||
|
||||
setDropdownValue(timezone) {
|
||||
this.$dropdownToggle.text(this.displayFormat(timezone));
|
||||
this.$input.val(timezone.name);
|
||||
this.$input.val(timezone.identifier);
|
||||
}
|
||||
|
||||
handleDropdownChange({ selectedObj, e }) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlButtonGroup, GlButton, GlModalDirective } from '@gitlab/ui';
|
||||
import { GlButtonGroup, GlButton } from '@gitlab/ui';
|
||||
import { uniqueId } from 'lodash';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
@ -20,9 +20,6 @@ export default {
|
|||
DeleteBlobModal,
|
||||
LockButton: () => import('ee_component/repository/components/lock_button.vue'),
|
||||
},
|
||||
directives: {
|
||||
GlModal: GlModalDirective,
|
||||
},
|
||||
mixins: [getRefMixin, glFeatureFlagMixin()],
|
||||
inject: {
|
||||
targetBranch: {
|
||||
|
@ -73,6 +70,10 @@ export default {
|
|||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
showForkSuggestion: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
replaceModalId() {
|
||||
|
@ -91,6 +92,16 @@ export default {
|
|||
return this.canLock ? 'lock_button' : 'disabled_lock_button';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showModal(modalId) {
|
||||
if (this.showForkSuggestion) {
|
||||
this.$emit('fork');
|
||||
return;
|
||||
}
|
||||
|
||||
this.$refs[modalId].show();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -107,14 +118,15 @@ export default {
|
|||
data-testid="lock"
|
||||
:data-qa-selector="lockBtnQASelector"
|
||||
/>
|
||||
<gl-button v-gl-modal="replaceModalId" data-testid="replace">
|
||||
<gl-button data-testid="replace" @click="showModal(replaceModalId)">
|
||||
{{ $options.i18n.replace }}
|
||||
</gl-button>
|
||||
<gl-button v-gl-modal="deleteModalId" data-testid="delete">
|
||||
<gl-button data-testid="delete" @click="showModal(deleteModalId)">
|
||||
{{ $options.i18n.delete }}
|
||||
</gl-button>
|
||||
</gl-button-group>
|
||||
<upload-blob-modal
|
||||
:ref="replaceModalId"
|
||||
:modal-id="replaceModalId"
|
||||
:modal-title="replaceModalTitle"
|
||||
:commit-message="replaceModalTitle"
|
||||
|
@ -126,6 +138,7 @@ export default {
|
|||
:primary-btn-text="$options.i18n.replacePrimaryBtnText"
|
||||
/>
|
||||
<delete-blob-modal
|
||||
:ref="deleteModalId"
|
||||
:modal-id="deleteModalId"
|
||||
:modal-title="deleteModalTitle"
|
||||
:delete-path="deletePath"
|
||||
|
|
|
@ -280,6 +280,8 @@ export default {
|
|||
:project-path="projectPath"
|
||||
:is-locked="Boolean(pathLockedByUser)"
|
||||
:can-lock="canLock"
|
||||
:show-fork-suggestion="showForkSuggestion"
|
||||
@fork="setForkTarget('ide')"
|
||||
/>
|
||||
</template>
|
||||
</blob-header>
|
||||
|
|
|
@ -146,6 +146,9 @@ export default {
|
|||
/* eslint-enable dot-notation */
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.$refs[this.modalId].show();
|
||||
},
|
||||
submitForm(e) {
|
||||
e.preventDefault(); // Prevent modal from closing
|
||||
this.form.showValidation = true;
|
||||
|
@ -164,6 +167,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-modal
|
||||
:ref="modalId"
|
||||
v-bind="$attrs"
|
||||
data-testid="modal-delete"
|
||||
:modal-id="modalId"
|
||||
|
|
|
@ -32,6 +32,7 @@ export default {
|
|||
class="gl-mr-3"
|
||||
category="secondary"
|
||||
variant="confirm"
|
||||
data-method="post"
|
||||
:href="forkPath"
|
||||
data-testid="fork"
|
||||
>
|
||||
|
|
|
@ -136,6 +136,9 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.$refs[this.modalId].show();
|
||||
},
|
||||
setFile(file) {
|
||||
this.file = file;
|
||||
|
||||
|
@ -206,6 +209,7 @@ export default {
|
|||
<template>
|
||||
<gl-form>
|
||||
<gl-modal
|
||||
:ref="modalId"
|
||||
:modal-id="modalId"
|
||||
:title="modalTitle"
|
||||
:action-primary="primaryOptions"
|
||||
|
|
|
@ -87,13 +87,7 @@ class GroupDescendantsFinder
|
|||
visible_to_user = visible_to_user.or(authorized_to_user)
|
||||
end
|
||||
|
||||
group_to_query = if Feature.enabled?(:linear_group_descendants_finder, current_user, default_enabled: :yaml)
|
||||
parent_group
|
||||
else
|
||||
hierarchy_for_parent
|
||||
end
|
||||
|
||||
group_to_query.descendants.where(visible_to_user)
|
||||
parent_group.descendants.where(visible_to_user)
|
||||
# rubocop: enable CodeReuse/Finder
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
@ -159,13 +153,7 @@ class GroupDescendantsFinder
|
|||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def projects_matching_filter
|
||||
# rubocop: disable CodeReuse/Finder
|
||||
objects_in_hierarchy = if Feature.enabled?(:linear_group_descendants_finder, current_user, default_enabled: :yaml)
|
||||
parent_group.self_and_descendants.as_ids
|
||||
else
|
||||
hierarchy_for_parent.base_and_descendants.select(:id)
|
||||
end
|
||||
|
||||
projects_nested_in_group = Project.where(namespace_id: objects_in_hierarchy)
|
||||
projects_nested_in_group = Project.where(namespace_id: parent_group.self_and_descendants.as_ids)
|
||||
params_with_search = params.merge(search: params[:filter])
|
||||
|
||||
ProjectsFinder.new(params: params_with_search,
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: linear_group_descendants_finder
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68954
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339440
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_cmark_renderer
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61792
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345744
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: true
|
|
@ -17,16 +17,10 @@ requests, and:
|
|||
- Take action on individual merge requests.
|
||||
- Reduce overall cycle time.
|
||||
|
||||
NOTE:
|
||||
Initially, no data appears. Data is populated as users comment on open merge requests.
|
||||
|
||||
## Overview
|
||||
|
||||
Code Review Analytics is available to users with at least the Reporter [role](../permissions.md), and displays a table of open merge requests that have at least one non-author comment. The review time is measured from the time the first non-author comment was submitted.
|
||||
|
||||
To access Code Review Analytics, from your project's menu, go to **Analytics > Code Review**.
|
||||
|
||||
You can filter the list of merge requests by milestone and label.
|
||||
NOTE:
|
||||
Initially, no data appears. Data is populated as users comment on open merge requests.
|
||||
|
||||
![Code Review Analytics](img/code_review_analytics_v13_11.png "List of code reviews; oldest review first.")
|
||||
|
||||
|
@ -36,6 +30,14 @@ The table is sorted by:
|
|||
or to be broken down into smaller parts.
|
||||
- Other columns: Display the author, approvers, comment count, and line change (-/+) counts.
|
||||
|
||||
## View Code Review Analytics
|
||||
|
||||
To view Code Review Analytics:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Analytics > Code Review**.
|
||||
1. Filter merge requests by milestone and label.
|
||||
|
||||
## Use cases
|
||||
|
||||
This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#delaney-development-team-lead)
|
||||
|
|
|
@ -87,7 +87,8 @@ Commit message templates support these variables:
|
|||
| `%{merged_by}` | User who merged the merge request. | `Alex Garcia <agarcia@example.com>` |
|
||||
| `%{co_authored_by}` | Names and emails of commit authors in a `Co-authored-by` Git commit trailer format. Limited to authors of 100 most recent commits in merge request. | `Co-authored-by: Zane Doe <zdoe@example.com>` <br> `Co-authored-by: Blake Smith <bsmith@example.com>` |
|
||||
|
||||
Empty variables that are the only word in a line are removed, along with all newline characters preceding it.
|
||||
Any line containing only an empty variable is removed. If the line to be removed is both
|
||||
preceded and followed by an empty line, the preceding empty line is also removed.
|
||||
|
||||
## Related topics
|
||||
|
||||
|
|
|
@ -7,13 +7,14 @@ module Banzai
|
|||
# Footnotes are supported in CommonMark. However we were stripping
|
||||
# the ids during sanitization. Those are now allowed.
|
||||
#
|
||||
# Footnotes are numbered the same - the first one has `id=fn1`, the
|
||||
# second is `id=fn2`, etc. In order to allow footnotes when rendering
|
||||
# multiple markdown blocks on a page, we need to make each footnote
|
||||
# reference unique.
|
||||
#
|
||||
# Footnotes are numbered as an increasing integer starting at `1`.
|
||||
# The `id` associated with a footnote is based on the footnote reference
|
||||
# string. For example, `[^foot]` will generate `id="fn-foot"`.
|
||||
# In order to allow footnotes when rendering multiple markdown blocks
|
||||
# on a page, we need to make each footnote reference unique.
|
||||
|
||||
# This filter adds a random number to each footnote (the same number
|
||||
# can be used for a single render). So you get `id=fn1-4335` and `id=fn2-4335`.
|
||||
# can be used for a single render). So you get `id=fn-1-4335` and `id=fn-foot-4335`.
|
||||
#
|
||||
class FootnoteFilter < HTML::Pipeline::Filter
|
||||
FOOTNOTE_ID_PREFIX = 'fn-'
|
||||
|
@ -26,53 +27,24 @@ module Banzai
|
|||
CSS_FOOTNOTE = 'sup > a[data-footnote-ref]'
|
||||
XPATH_FOOTNOTE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_FOOTNOTE).freeze
|
||||
|
||||
# only needed when feature flag use_cmark_renderer is turned off
|
||||
INTEGER_PATTERN = /\A\d+\z/.freeze
|
||||
FOOTNOTE_ID_PREFIX_OLD = 'fn'
|
||||
FOOTNOTE_LINK_ID_PREFIX_OLD = 'fnref'
|
||||
FOOTNOTE_LI_REFERENCE_PATTERN_OLD = /\A#{FOOTNOTE_ID_PREFIX_OLD}\d+\z/.freeze
|
||||
FOOTNOTE_LINK_REFERENCE_PATTERN_OLD = /\A#{FOOTNOTE_LINK_ID_PREFIX_OLD}\d+\z/.freeze
|
||||
FOOTNOTE_START_NUMBER = 1
|
||||
CSS_SECTION_OLD = "ol > li[id=#{FOOTNOTE_ID_PREFIX_OLD}#{FOOTNOTE_START_NUMBER}]"
|
||||
XPATH_SECTION_OLD = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_SECTION_OLD).freeze
|
||||
|
||||
def call
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
# Sanitization stripped off the section class - add it back in
|
||||
return doc unless section_node = doc.at_xpath(XPATH_SECTION)
|
||||
# Sanitization stripped off the section class - add it back in
|
||||
return doc unless section_node = doc.at_xpath(XPATH_SECTION)
|
||||
|
||||
section_node.append_class('footnotes')
|
||||
else
|
||||
return doc unless first_footnote = doc.at_xpath(XPATH_SECTION_OLD)
|
||||
return doc unless first_footnote.parent
|
||||
|
||||
first_footnote.parent.wrap('<section class="footnotes">')
|
||||
end
|
||||
section_node.append_class('footnotes')
|
||||
|
||||
rand_suffix = "-#{random_number}"
|
||||
modified_footnotes = {}
|
||||
|
||||
xpath_footnote = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
XPATH_FOOTNOTE
|
||||
else
|
||||
Gitlab::Utils::Nokogiri.css_to_xpath('sup > a[id]')
|
||||
end
|
||||
doc.xpath(XPATH_FOOTNOTE).each do |link_node|
|
||||
ref_num = link_node[:id].delete_prefix(FOOTNOTE_LINK_ID_PREFIX)
|
||||
ref_num.gsub!(/[[:punct:]]/, '\\\\\&')
|
||||
|
||||
doc.xpath(xpath_footnote).each do |link_node|
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
ref_num = link_node[:id].delete_prefix(FOOTNOTE_LINK_ID_PREFIX)
|
||||
ref_num.gsub!(/[[:punct:]]/, '\\\\\&')
|
||||
else
|
||||
ref_num = link_node[:id].delete_prefix(FOOTNOTE_LINK_ID_PREFIX_OLD)
|
||||
end
|
||||
|
||||
css = Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) ? "section[data-footnotes] li[id=#{fn_id(ref_num)}]" : "li[id=#{fn_id(ref_num)}]"
|
||||
css = "section[data-footnotes] li[id=#{fn_id(ref_num)}]"
|
||||
node_xpath = Gitlab::Utils::Nokogiri.css_to_xpath(css)
|
||||
footnote_node = doc.at_xpath(node_xpath)
|
||||
|
||||
if footnote_node || modified_footnotes[ref_num]
|
||||
next if Feature.disabled?(:use_cmark_renderer, default_enabled: :yaml) && !INTEGER_PATTERN.match?(ref_num)
|
||||
|
||||
link_node[:href] += rand_suffix
|
||||
link_node[:id] += rand_suffix
|
||||
|
||||
|
@ -103,13 +75,11 @@ module Banzai
|
|||
end
|
||||
|
||||
def fn_id(num)
|
||||
prefix = Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) ? FOOTNOTE_ID_PREFIX : FOOTNOTE_ID_PREFIX_OLD
|
||||
"#{prefix}#{num}"
|
||||
"#{FOOTNOTE_ID_PREFIX}#{num}"
|
||||
end
|
||||
|
||||
def fnref_id(num)
|
||||
prefix = Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) ? FOOTNOTE_LINK_ID_PREFIX : FOOTNOTE_LINK_ID_PREFIX_OLD
|
||||
"#{prefix}#{num}"
|
||||
"#{FOOTNOTE_LINK_ID_PREFIX}#{num}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
# This module is used in Banzai::Filter::MarkdownFilter.
|
||||
# Used gem is `commonmarker` which is a ruby wrapper for libcmark (CommonMark parser)
|
||||
# including GitHub's GFM extensions.
|
||||
# We now utilize the renderer built in `C`, rather than the ruby based renderer.
|
||||
# Homepage: https://github.com/gjtorikian/commonmarker
|
||||
|
||||
module Banzai
|
||||
module Filter
|
||||
module MarkdownEngines
|
||||
|
@ -22,57 +22,29 @@ module Banzai
|
|||
:VALIDATE_UTF8 # replace illegal sequences with the replacement character U+FFFD.
|
||||
].freeze
|
||||
|
||||
RENDER_OPTIONS_C = [
|
||||
RENDER_OPTIONS = [
|
||||
:GITHUB_PRE_LANG, # use GitHub-style <pre lang> for fenced code blocks.
|
||||
:FOOTNOTES, # render footnotes.
|
||||
:FULL_INFO_STRING, # include full info strings of code blocks in separate attribute.
|
||||
:UNSAFE # allow raw/custom HTML and unsafe links.
|
||||
].freeze
|
||||
|
||||
# The `:GITHUB_PRE_LANG` option is not used intentionally because
|
||||
# it renders a fence block with language as `<pre lang="LANG"><code>some code\n</code></pre>`
|
||||
# while GitLab's syntax is `<pre><code lang="LANG">some code\n</code></pre>`.
|
||||
# If in the future the syntax is about to be made GitHub-compatible, please, add `:GITHUB_PRE_LANG` render option below
|
||||
# and remove `code_block` method from `lib/banzai/renderer/common_mark/html.rb`.
|
||||
RENDER_OPTIONS_RUBY = [
|
||||
# as of commonmarker 0.18.0, we need to use :UNSAFE to get the same as the original :DEFAULT
|
||||
# https://github.com/gjtorikian/commonmarker/pull/81
|
||||
:UNSAFE # allow raw/custom HTML and unsafe links.
|
||||
].freeze
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
@renderer = Banzai::Renderer::CommonMark::HTML.new(options: render_options) if Feature.disabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def render(text)
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
CommonMarker.render_html(text, render_options, extensions)
|
||||
else
|
||||
doc = CommonMarker.render_doc(text, PARSE_OPTIONS, extensions)
|
||||
|
||||
@renderer.render(doc)
|
||||
end
|
||||
CommonMarker.render_html(text, render_options, EXTENSIONS)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extensions
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
EXTENSIONS
|
||||
else
|
||||
EXTENSIONS + [
|
||||
:tagfilter # strips out several "unsafe" HTML tags from being used: https://github.github.com/gfm/#disallowed-raw-html-extension-
|
||||
].freeze
|
||||
end
|
||||
end
|
||||
|
||||
def render_options
|
||||
@context[:no_sourcepos] ? render_options_no_sourcepos : render_options_sourcepos
|
||||
end
|
||||
|
||||
def render_options_no_sourcepos
|
||||
Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) ? RENDER_OPTIONS_C : RENDER_OPTIONS_RUBY
|
||||
RENDER_OPTIONS
|
||||
end
|
||||
|
||||
def render_options_sourcepos
|
||||
|
|
|
@ -8,8 +8,10 @@ module Banzai
|
|||
NOT_LITERAL_REGEX = %r{#{LITERAL_KEYWORD}-((%5C|\\).+?)-#{LITERAL_KEYWORD}}.freeze
|
||||
SPAN_REGEX = %r{<span>(.*?)</span>}.freeze
|
||||
|
||||
CSS_A = 'a'
|
||||
XPATH_A = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_A).freeze
|
||||
CSS_A = 'a'
|
||||
XPATH_A = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_A).freeze
|
||||
CSS_LANG_TAG = 'pre'
|
||||
XPATH_LANG_TAG = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_LANG_TAG).freeze
|
||||
|
||||
def call
|
||||
return doc unless result[:escaped_literals]
|
||||
|
@ -32,22 +34,12 @@ module Banzai
|
|||
node.attributes['title'].value = node.attributes['title'].value.gsub(SPAN_REGEX, '\1') if node.attributes['title']
|
||||
end
|
||||
|
||||
doc.xpath(lang_tag).each do |node|
|
||||
doc.xpath(XPATH_LANG_TAG).each do |node|
|
||||
node.attributes['lang'].value = node.attributes['lang'].value.gsub(SPAN_REGEX, '\1') if node.attributes['lang']
|
||||
end
|
||||
|
||||
doc
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def lang_tag
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
Gitlab::Utils::Nokogiri.css_to_xpath('pre')
|
||||
else
|
||||
Gitlab::Utils::Nokogiri.css_to_xpath('code')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,12 +25,7 @@ module Banzai
|
|||
private
|
||||
|
||||
def lang_tag
|
||||
@lang_tag ||=
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
Gitlab::Utils::Nokogiri.css_to_xpath('pre[lang="plantuml"] > code').freeze
|
||||
else
|
||||
Gitlab::Utils::Nokogiri.css_to_xpath('pre > code[lang="plantuml"]').freeze
|
||||
end
|
||||
@lang_tag ||= Gitlab::Utils::Nokogiri.css_to_xpath('pre[lang="plantuml"] > code').freeze
|
||||
end
|
||||
|
||||
def settings
|
||||
|
|
|
@ -28,12 +28,10 @@ module Banzai
|
|||
allowlist[:attributes]['li'] = %w[id]
|
||||
allowlist[:transformers].push(self.class.remove_non_footnote_ids)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
# Allow section elements with data-footnotes attribute
|
||||
allowlist[:elements].push('section')
|
||||
allowlist[:attributes]['section'] = %w(data-footnotes)
|
||||
allowlist[:attributes]['a'].push('data-footnote-ref', 'data-footnote-backref')
|
||||
end
|
||||
# Allow section elements with data-footnotes attribute
|
||||
allowlist[:elements].push('section')
|
||||
allowlist[:attributes]['section'] = %w(data-footnotes)
|
||||
allowlist[:attributes]['a'].push('data-footnote-ref', 'data-footnote-backref')
|
||||
|
||||
allowlist
|
||||
end
|
||||
|
@ -61,13 +59,8 @@ module Banzai
|
|||
return unless node.name == 'a' || node.name == 'li'
|
||||
return unless node.has_attribute?('id')
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
return if node.name == 'a' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LINK_REFERENCE_PATTERN
|
||||
return if node.name == 'li' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LI_REFERENCE_PATTERN
|
||||
else
|
||||
return if node.name == 'a' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LINK_REFERENCE_PATTERN_OLD
|
||||
return if node.name == 'li' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LI_REFERENCE_PATTERN_OLD
|
||||
end
|
||||
return if node.name == 'a' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LINK_REFERENCE_PATTERN
|
||||
return if node.name == 'li' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LI_REFERENCE_PATTERN
|
||||
|
||||
node.remove_attribute('id')
|
||||
end
|
||||
|
|
|
@ -70,11 +70,11 @@ module Banzai
|
|||
private
|
||||
|
||||
def parse_lang_params(node)
|
||||
node = node.parent if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
node = node.parent
|
||||
|
||||
# Commonmarker's FULL_INFO_STRING render option works with the space delimiter.
|
||||
# But the current behavior of GitLab's markdown renderer is different - it grabs everything as the single
|
||||
# line, including language and its options. To keep backward compatability, we have to parse the old format and
|
||||
# line, including language and its options. To keep backward compatibility, we have to parse the old format and
|
||||
# merge with the new one.
|
||||
#
|
||||
# Behaviors before separating language and its parameters:
|
||||
|
@ -91,11 +91,7 @@ module Banzai
|
|||
return unless language
|
||||
|
||||
language, language_params = language.split(LANG_PARAMS_DELIMITER, 2)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
language_params = [node.attr('data-meta'), language_params].compact.join(' ')
|
||||
end
|
||||
|
||||
language_params = [node.attr('data-meta'), language_params].compact.join(' ')
|
||||
formatted_language_params = format_language_params(language_params)
|
||||
|
||||
[language, formatted_language_params]
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Remove this entire file when removing `use_cmark_renderer` feature flag and switching to the CMARK html renderer.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/345744
|
||||
module Banzai
|
||||
module Renderer
|
||||
module CommonMark
|
||||
class HTML < CommonMarker::HtmlRenderer
|
||||
def code_block(node)
|
||||
block do
|
||||
out("<pre#{sourcepos(node)}><code")
|
||||
out(' lang="', node.fence_info, '"') if node.fence_info.present?
|
||||
out('>')
|
||||
out(escape_html(node.string_content))
|
||||
out('</code></pre>')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,11 +7,7 @@ module Gitlab
|
|||
register_for 'gitlab-html-pipeline'
|
||||
|
||||
def format(node, lang, opts)
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
%(<pre #{lang ? %[lang="#{lang}"] : ''}><code>#{node.content}</code></pre>)
|
||||
else
|
||||
%(<pre><code #{lang ? %[ lang="#{lang}"] : ''}>#{node.content}</code></pre>)
|
||||
end
|
||||
%(<pre #{lang ? %[lang="#{lang}"] : ''}><code>#{node.content}</code></pre>)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,9 +65,13 @@ module Gitlab
|
|||
end
|
||||
names_of_empty_variables = values.filter_map { |name, value| name if value.blank? }
|
||||
|
||||
# Remove placeholders that correspond to empty values and are the only word in a line
|
||||
# along with all whitespace characters preceding them.
|
||||
message = message.gsub(/[\n\r]+#{Regexp.union(names_of_empty_variables)}$/, '') if names_of_empty_variables.present?
|
||||
# Remove lines that contain empty variable placeholder and nothing else.
|
||||
if names_of_empty_variables.present?
|
||||
# If there is blank line or EOF after it, remove blank line before it as well.
|
||||
message = message.gsub(/\n\n#{Regexp.union(names_of_empty_variables)}(\n\n|\Z)/, '\1')
|
||||
# Otherwise, remove only the line it is in.
|
||||
message = message.gsub(/^#{Regexp.union(names_of_empty_variables)}\n/, '')
|
||||
end
|
||||
# Substitute all variables with their values.
|
||||
message = message.gsub(Regexp.union(values.keys), values) if values.present?
|
||||
|
||||
|
|
|
@ -1,209 +1,199 @@
|
|||
{
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 4.835599899291992,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 26.39240026473999,
|
||||
"qa/specs/features/ee/browser_ui/secure/create_project_with_secure_spec.rb": 46.76790499687195,
|
||||
"qa/specs/features/api/1_manage/users_spec.rb": 0.6089541912078857,
|
||||
"qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb": 20.58603596687317,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb": 31.49540114402771,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb": 16.18057894706726,
|
||||
"qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb": 18.047621726989746,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 19.459370374679565,
|
||||
"qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb": 22.800872802734375,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb": 5.812374591827393,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb": 17.273863554000854,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb": 8.31815505027771,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 51.81568956375122,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 30.373554468154907,
|
||||
"qa/specs/features/sanity/version_spec.rb": 0.0004665851593017578,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 22.993898630142212,
|
||||
"qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb": 36.358747243881226,
|
||||
"qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb": 9.079580068588257,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 39.412545919418335,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 27.905837297439575,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 99.71209812164307,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 25.72262978553772,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 44.536749839782715,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 18.482256174087524,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 16.314701795578003,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 36.934654235839844,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 17.103047847747803,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 28.352823495864868,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 20.208287954330444,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 13.733941793441772,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 16.376311540603638,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 98.71593880653381,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 21.307689666748047,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 39.39231467247009,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 18.50350284576416,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 30.67392325401306,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 16.17888569831848,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 45.47245216369629,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 38.93913507461548,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 12.087258100509644,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 17.309232473373413,
|
||||
"qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb": 70.75156497955322,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 22.20275855064392,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 24.21539831161499,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 20.22646951675415,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 31.00749373435974,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 18.430193424224854,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb": 13.615312337875366,
|
||||
"qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb": 94.04865074157715,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 59.575963258743286,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/share_group_with_group_spec.rb": 25.6483793258667,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 26.07162046432495,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 8.41316819190979,
|
||||
"qa/specs/features/ee/browser_ui/secure/merge_request_license_widget_spec.rb": 73.56247329711914,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 6.235261917114258,
|
||||
"qa/specs/features/browser_ui/5_package/container_registry_spec.rb": 7.263134717941284,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 10.079012870788574,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 16.52791404724121,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 10.684799909591675,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 72.14465713500977,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 15.664107322692871,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 34.32576060295105,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb": 31.586787939071655,
|
||||
"qa/specs/features/ee/browser_ui/secure/project_security_dashboard_spec.rb": 27.6742160320282,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 11.836639165878296,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/user/minimal_access_user_spec.rb": 15.691015005111694,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 38.80854368209839,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 15.07186245918274,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 41.58929800987244,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 13.438591957092285,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 30.66688632965088,
|
||||
"qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb": 25.137597799301147,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 8.731743574142456,
|
||||
"qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb": 0.0008337497711181641,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 40.60329842567444,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 39.090237617492676,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb": 22.633376359939575,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb": 18.457061767578125,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb": 49.273433685302734,
|
||||
"qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb": 14.6923348903656,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 38.44519090652466,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 46.04780888557434,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb": 27.77385425567627,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/insights/default_insights_spec.rb": 28.850987195968628,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 19.598198413848877,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 15.17819595336914,
|
||||
"qa/specs/features/api/1_manage/user_access_termination_spec.rb": 6.3396782875061035,
|
||||
"qa/specs/features/ee/browser_ui/secure/create_merge_request_with_secure_spec.rb": 71.55253982543945,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb": 42.83795666694641,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 17.03721809387207,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb": 25.14606213569641,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb": 70.44876503944397,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb": 52.93111038208008,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 48.3312201499939,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_2_spec.rb": 100.1787781715393,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 22.37237572669983,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_file_template_spec.rb": 69.36870694160461,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 13.524356126785278,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/prevent_forking_outside_group_spec.rb": 37.17233395576477,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 6.892294406890869,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 12.628767013549805,
|
||||
"qa/specs/features/ee/browser_ui/secure/enable_sast_from_configuration_spec.rb": 86.38991785049438,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_for_project_mirror_github_spec.rb": 12.684113502502441,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 35.340261459350586,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 19.719850540161133,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 38.779794216156006,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 6.86094856262207,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 51.54451298713684,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 18.586050033569336,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb": 35.587294578552246,
|
||||
"qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb": 14.01547122001648,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb": 20.370421409606934,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb": 15.297787427902222,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 42.507469177246094,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_status_on_operation_dashboard_spec.rb": 46.77937316894531,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb": 20.96509575843811,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 12.62861156463623,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 49.85329723358154,
|
||||
"qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb": 84.63548684120178,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 15.584527254104614,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb": 30.72457480430603,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 10.70707631111145,
|
||||
"qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb": 4.82462477684021,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 32.49313712120056,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb": 32.37481236457825,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 16.573434114456177,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 43.15674066543579,
|
||||
"qa/specs/features/ee/browser_ui/secure/license_compliance_spec.rb": 31.000027179718018,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb": 149.64519357681274,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/restrict_by_ip_address_spec.rb": 116.07316851615906,
|
||||
"qa/specs/features/browser_ui/1_manage/login/register_spec.rb": 145.5431580543518,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 19.418848514556885,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 18.54582905769348,
|
||||
"qa/specs/features/api/5_package/container_registry_spec.rb": 3.93778920173645,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 148.70570707321167,
|
||||
"qa/specs/features/ee/browser_ui/6_release/multi-project_pipelines_spec.rb": 52.770936250686646,
|
||||
"qa/specs/features/ee/browser_ui/3_create/contribution_analytics_spec.rb": 45.81806302070618,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 20.386794805526733,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb": 21.968912363052368,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb": 71.7951602935791,
|
||||
"qa/specs/features/api/1_manage/bulk_import_group_spec.rb": 50.21021842956543,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 16.42144775390625,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 173.84056043624878,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/project/project_templates_spec.rb": 86.48439168930054,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 56.275856018066406,
|
||||
"qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 87.7243127822876,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 98.70601177215576,
|
||||
"qa/specs/features/ee/browser_ui/secure/vulnerability_management_spec.rb": 91.42165040969849,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 176.7654173374176,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 17.273313283920288,
|
||||
"qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb": 15.879833459854126,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 16.24162793159485,
|
||||
"qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb": 55.61779570579529,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 15.482072830200195,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 54.22519111633301,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 85.68487024307251,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/web_terminal_spec.rb": 63.027522802352905,
|
||||
"qa/specs/features/ee/browser_ui/3_create/wiki/create_group_wiki_page_spec.rb": 26.17377734184265,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb": 16.853343725204468,
|
||||
"qa/specs/features/api/1_manage/project_access_token_spec.rb": 2.8215739727020264,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb": 109.0079870223999,
|
||||
"qa/specs/features/browser_ui/6_release/pages/pages_pipeline_spec.rb": 73.78986692428589,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb": 34.84124970436096,
|
||||
"qa/specs/features/api/1_manage/import_github_repo_spec.rb": 11.330891609191895,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 36.70389533042908,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 25.407151699066162,
|
||||
"qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb": 109.88336181640625,
|
||||
"qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb": 24.014917373657227,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 42.596492767333984,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 60.067442893981934,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 185.41551113128662,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 30.03315234184265,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 76.58771228790283,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 28.64104723930359,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 198.37600350379944,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb": 35.098846435546875,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 51.25122785568237,
|
||||
"qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb": 61.072656869888306,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 135.90494751930237,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/cancelling_merge_request_in_merge_train_spec.rb": 59.57308530807495,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb": 32.462252140045166,
|
||||
"qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb": 53.246745586395264,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb": 79.99787259101868,
|
||||
"qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb": 57.00465273857117,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 77.2134940624237,
|
||||
"qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb": 48.472514390945435,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 17.153425455093384,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb": 96.45902276039124,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb": 64.81094217300415,
|
||||
"qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb": 27.902702569961548,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb": 23.476325750350952,
|
||||
"qa/specs/features/ee/browser_ui/10_protect/policy_list_spec.rb": 14.327334642410278,
|
||||
"qa/specs/features/ee/browser_ui/secure/security_reports_spec.rb": 26.599382877349854,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 39.33750772476196,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb": 50.194467306137085,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 59.395915508270264,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipelines_for_merged_results_and_merge_trains_spec.rb": 203.04975271224976,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 42.03165292739868,
|
||||
"qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb": 18.495836973190308,
|
||||
"qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb": 17.1261248588562,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 53.17536449432373,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 62.42040038108826,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 90.16095304489136,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb": 91.91785287857056,
|
||||
"qa/specs/features/browser_ui/5_package/maven_repository_spec.rb": 687.8482820987701,
|
||||
"qa/specs/features/browser_ui/5_package/npm_registry_spec.rb": 225.33412504196167
|
||||
"qa/specs/features/api/1_manage/users_spec.rb": 0.38025754499994946,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 6.1726223529999515,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 6.837727864000044,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 6.9398801430002095,
|
||||
"qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb": 7.072401565999826,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 8.430185218000133,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 9.21629216500014,
|
||||
"qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb": 9.460245247999865,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 9.749626129999797,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 9.997606156000074,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 10.100938496999788,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 10.435720339999989,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 10.664103456999783,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb": 11.010621653000044,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 11.080987325000024,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 11.289408692999814,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 11.388780440000119,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 11.812601945000097,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 11.884903447000397,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 11.973681912000075,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb": 11.982320482999967,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 12.454461346999778,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 12.595205497999814,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 12.696943737999845,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 12.821059261999835,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 13.493086933000086,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 13.522706569999855,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 13.611114558000281,
|
||||
"qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb": 13.662936730000183,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 13.997128957999848,
|
||||
"qa/specs/features/ee/browser_ui/10_protect/policy_alerts_list_spec.rb": 14.055217947000074,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 14.212461153999811,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 14.218627059000028,
|
||||
"qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb": 14.295524570999987,
|
||||
"qa/specs/features/api/1_manage/project_access_token_spec.rb": 14.394589879999785,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 14.505683429000328,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 14.804579386000114,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb": 14.869172961000004,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 15.069100258000162,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 15.071375434999936,
|
||||
"qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb": 15.238976046000062,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 15.25893454900006,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 15.62486792799973,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 15.73652188899996,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 16.175388279000117,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 16.21438098999988,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 16.22156817799987,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 16.28064358199981,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 16.526415993999763,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb": 16.635663933999695,
|
||||
"qa/specs/features/api/1_manage/import_github_repo_spec.rb": 16.75859540500005,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 16.85444325000026,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 17.065811306999876,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/purchase/user_registration_billing_spec.rb": 17.17362991999994,
|
||||
"qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb": 17.35990919599999,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 17.4036377489997,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 17.53151273000003,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb": 18.336858511999708,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb": 18.827946458000042,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 19.011096268000074,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 19.273840846999974,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 19.779615910000075,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb": 19.787533178000103,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 20.357167043000118,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 20.58807039300018,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 20.604954283000097,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 20.84536794700034,
|
||||
"qa/specs/features/ee/browser_ui/3_create/wiki/create_group_wiki_page_spec.rb": 21.285323852000147,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb": 21.324911325999892,
|
||||
"qa/specs/features/api/1_manage/user_access_termination_spec.rb": 21.359333020000122,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb": 21.53934234799999,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 21.623363159999826,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb": 21.639708403999975,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb": 22.508069357000295,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb": 22.67048247999992,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/insights/default_insights_spec.rb": 22.707359102999817,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 23.914098683999782,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/share_group_with_group_spec.rb": 24.228926759999922,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 24.320835930999692,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb": 24.421617836999985,
|
||||
"qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb": 24.568071621999934,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 25.105601008999656,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 25.670671182000206,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 26.588750723999965,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 26.957921672999873,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 27.038084878000063,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/user/minimal_access_user_spec.rb": 27.164655879999827,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 27.958475461000035,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 29.15091494699982,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 29.63945763499987,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb": 30.055674564000128,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 30.36701765900034,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 31.11626907400023,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb": 31.399775493000107,
|
||||
"qa/specs/features/ee/browser_ui/10_protect/policies_list_spec.rb": 31.579298365999875,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 31.699020851000114,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 32.22923225600016,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb": 32.390305334999994,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 32.4001524549999,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb": 32.588556773000164,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb": 32.655282309000086,
|
||||
"qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb": 32.67606646000013,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 32.86781491000011,
|
||||
"qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb": 33.592668581,
|
||||
"qa/specs/features/browser_ui/14_non_devops/performance_bar_spec.rb": 33.844586084000184,
|
||||
"qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb": 34.22076284800005,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 34.69707643500033,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 35.48510294100015,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 35.60547228999985,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb": 35.720513429999755,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 36.74466744499978,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 36.86498900599986,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 36.88383336300012,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb": 36.91027954099991,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 38.008783447999576,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 38.2163332099999,
|
||||
"qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb": 38.41997747000005,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 38.798842664000176,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 38.86858395499985,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 39.11634360200014,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/prevent_forking_outside_group_spec.rb": 39.33498874499992,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 39.408525157999975,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb": 39.66159539199998,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_status_on_operation_dashboard_spec.rb": 39.74350435799988,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 39.873689517,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 41.68991280600039,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 42.34842452200019,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 42.87835724299998,
|
||||
"qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb": 43.00558933000002,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 45.002151569000034,
|
||||
"qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb": 45.135388796999905,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 45.27653511099993,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 45.49861126799988,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 45.95718983799998,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 46.274925919,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 47.46850344200038,
|
||||
"qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb": 47.98207172000002,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb": 48.11739431199976,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 48.333632795000085,
|
||||
"qa/specs/features/ee/browser_ui/6_release/multi-project_pipelines_spec.rb": 49.07156694399987,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 49.98554557300031,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 50.13713323600041,
|
||||
"qa/specs/features/ee/api/1_manage/bulk_import_group_spec.rb": 50.35492376299999,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 50.79862357000002,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb": 51.18897452500005,
|
||||
"qa/specs/features/ee/browser_ui/3_create/contribution_analytics_spec.rb": 51.5182317230001,
|
||||
"qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb": 52.09437633100015,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb": 53.78650449299994,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 53.991341565000084,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/create_merge_request_with_secure_spec.rb": 56.61833271799969,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 58.94148023099979,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 59.88873274600019,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 61.892666940000254,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb": 64.76709299799995,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 65.38459789100034,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/merge_request_license_widget_spec.rb": 65.64657268999963,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 66.96891640700005,
|
||||
"qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb": 68.11150705099999,
|
||||
"qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb": 70.67798023099999,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb": 71.9568110780001,
|
||||
"qa/specs/features/browser_ui/6_release/pages/pages_pipeline_spec.rb": 73.23625617499965,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_file_template_spec.rb": 74.34492043599994,
|
||||
"qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb": 75.80247018900013,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 75.8654343979997,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 76.87336582600028,
|
||||
"qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb": 82.25273063899976,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 82.48331730200016,
|
||||
"qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb": 84.2234556809999,
|
||||
"qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 86.64432922500009,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/project/project_templates_spec.rb": 89.27986745599992,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb": 90.51838889500004,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/cancelling_merge_request_in_merge_train_spec.rb": 99.40036980900004,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb": 99.88996194999982,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 100.75571312800002,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 102.73961456400002,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipelines_for_merged_results_and_merge_trains_spec.rb": 108.17952484600005,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb": 108.78527251000014,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/restrict_by_ip_address_spec.rb": 108.98986279500014,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 109.23582328299972,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 117.43055826199998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 120.7805862900002,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/security_reports_spec.rb": 121.40739863099998,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 121.93682936799996,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_2_spec.rb": 124.35619795699995,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/instance/instance_audit_logs_spec.rb": 129.314630158,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/vulnerability_management_spec.rb": 132.3623656059999,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb": 135.19990866599983,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 136.47575045699978,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/project_security_dashboard_spec.rb": 137.37273152800026,
|
||||
"qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb": 154.7787124900001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 161.17543870600002,
|
||||
"qa/specs/features/api/1_manage/bulk_import_group_spec.rb": 167.9717091839998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 192.326786567,
|
||||
"qa/specs/features/browser_ui/1_manage/login/register_spec.rb": 412.09824056499974,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 428.72626845000013,
|
||||
"qa/specs/features/api/1_manage/bulk_import_project_spec.rb": 686.55653471,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 923.7095398920001
|
||||
}
|
||||
|
|
|
@ -17,262 +17,250 @@ RSpec.describe GroupDescendantsFinder do
|
|||
described_class.new(current_user: user, parent_group: group, params: params)
|
||||
end
|
||||
|
||||
shared_examples 'group descentants finder examples' do
|
||||
describe '#has_children?' do
|
||||
describe '#has_children?' do
|
||||
it 'is true when there are projects' do
|
||||
create(:project, namespace: group)
|
||||
|
||||
expect(finder.has_children?).to be_truthy
|
||||
end
|
||||
|
||||
context 'when there are subgroups' do
|
||||
it 'is true when there are projects' do
|
||||
create(:project, namespace: group)
|
||||
create(:group, parent: group)
|
||||
|
||||
expect(finder.has_children?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are subgroups' do
|
||||
it 'is true when there are projects' do
|
||||
create(:group, parent: group)
|
||||
describe '#execute' do
|
||||
it 'includes projects' do
|
||||
project = create(:project, namespace: group)
|
||||
|
||||
expect(finder.has_children?).to be_truthy
|
||||
end
|
||||
expect(finder.execute).to contain_exactly(project)
|
||||
end
|
||||
|
||||
context 'when archived is `true`' do
|
||||
let(:params) { { archived: 'true' } }
|
||||
|
||||
it 'includes archived projects' do
|
||||
archived_project = create(:project, namespace: group, archived: true)
|
||||
project = create(:project, namespace: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(archived_project, project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
it 'includes projects' do
|
||||
project = create(:project, namespace: group)
|
||||
context 'when archived is `only`' do
|
||||
let(:params) { { archived: 'only' } }
|
||||
|
||||
expect(finder.execute).to contain_exactly(project)
|
||||
it 'includes only archived projects' do
|
||||
archived_project = create(:project, namespace: group, archived: true)
|
||||
_project = create(:project, namespace: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(archived_project)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include archived projects' do
|
||||
_archived_project = create(:project, :archived, namespace: group)
|
||||
|
||||
expect(finder.execute).to be_empty
|
||||
end
|
||||
|
||||
context 'with a filter' do
|
||||
let(:params) { { filter: 'test' } }
|
||||
|
||||
it 'includes only projects matching the filter' do
|
||||
_other_project = create(:project, namespace: group)
|
||||
matching_project = create(:project, namespace: group, name: 'testproject')
|
||||
|
||||
expect(finder.execute).to contain_exactly(matching_project)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sorts elements by name as default' do
|
||||
project1 = create(:project, namespace: group, name: 'z')
|
||||
project2 = create(:project, namespace: group, name: 'a')
|
||||
|
||||
expect(subject.execute).to match_array([project2, project1])
|
||||
end
|
||||
|
||||
context 'sorting by name' do
|
||||
let!(:project1) { create(:project, namespace: group, name: 'a', path: 'project-a') }
|
||||
let!(:project2) { create(:project, namespace: group, name: 'z', path: 'project-z') }
|
||||
let(:params) do
|
||||
{
|
||||
sort: 'name_asc'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when archived is `true`' do
|
||||
let(:params) { { archived: 'true' } }
|
||||
|
||||
it 'includes archived projects' do
|
||||
archived_project = create(:project, namespace: group, archived: true)
|
||||
project = create(:project, namespace: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(archived_project, project)
|
||||
end
|
||||
it 'sorts elements by name' do
|
||||
expect(subject.execute).to eq(
|
||||
[
|
||||
project1,
|
||||
project2
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
context 'when archived is `only`' do
|
||||
let(:params) { { archived: 'only' } }
|
||||
|
||||
it 'includes only archived projects' do
|
||||
archived_project = create(:project, namespace: group, archived: true)
|
||||
_project = create(:project, namespace: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(archived_project)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include archived projects' do
|
||||
_archived_project = create(:project, :archived, namespace: group)
|
||||
|
||||
expect(finder.execute).to be_empty
|
||||
end
|
||||
|
||||
context 'with a filter' do
|
||||
let(:params) { { filter: 'test' } }
|
||||
|
||||
it 'includes only projects matching the filter' do
|
||||
_other_project = create(:project, namespace: group)
|
||||
matching_project = create(:project, namespace: group, name: 'testproject')
|
||||
|
||||
expect(finder.execute).to contain_exactly(matching_project)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sorts elements by name as default' do
|
||||
project1 = create(:project, namespace: group, name: 'z')
|
||||
project2 = create(:project, namespace: group, name: 'a')
|
||||
|
||||
expect(subject.execute).to match_array([project2, project1])
|
||||
end
|
||||
|
||||
context 'sorting by name' do
|
||||
let!(:project1) { create(:project, namespace: group, name: 'a', path: 'project-a') }
|
||||
let!(:project2) { create(:project, namespace: group, name: 'z', path: 'project-z') }
|
||||
let(:params) do
|
||||
{
|
||||
sort: 'name_asc'
|
||||
}
|
||||
end
|
||||
context 'with nested groups' do
|
||||
let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') }
|
||||
let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') }
|
||||
|
||||
it 'sorts elements by name' do
|
||||
expect(subject.execute).to eq(
|
||||
[
|
||||
subgroup1,
|
||||
subgroup2,
|
||||
project1,
|
||||
project2
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
context 'with nested groups' do
|
||||
let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') }
|
||||
let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') }
|
||||
|
||||
it 'sorts elements by name' do
|
||||
expect(subject.execute).to eq(
|
||||
[
|
||||
subgroup1,
|
||||
subgroup2,
|
||||
project1,
|
||||
project2
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include projects shared with the group' do
|
||||
project = create(:project, namespace: group)
|
||||
other_project = create(:project)
|
||||
other_project.project_group_links.create!(group: group,
|
||||
group_access: Gitlab::Access::MAINTAINER)
|
||||
|
||||
expect(finder.execute).to contain_exactly(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with shared groups' do
|
||||
let_it_be(:other_group) { create(:group) }
|
||||
let_it_be(:shared_group_link) do
|
||||
create(:group_group_link,
|
||||
shared_group: group,
|
||||
shared_with_group: other_group)
|
||||
end
|
||||
it 'does not include projects shared with the group' do
|
||||
project = create(:project, namespace: group)
|
||||
other_project = create(:project)
|
||||
other_project.project_group_links.create!(group: group,
|
||||
group_access: Gitlab::Access::MAINTAINER)
|
||||
|
||||
context 'without common ancestor' do
|
||||
expect(finder.execute).to contain_exactly(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with shared groups' do
|
||||
let_it_be(:other_group) { create(:group) }
|
||||
let_it_be(:shared_group_link) do
|
||||
create(:group_group_link,
|
||||
shared_group: group,
|
||||
shared_with_group: other_group)
|
||||
end
|
||||
|
||||
context 'without common ancestor' do
|
||||
it { expect(finder.execute).to be_empty }
|
||||
end
|
||||
|
||||
context 'with common ancestor' do
|
||||
let_it_be(:common_ancestor) { create(:group) }
|
||||
let_it_be(:other_group) { create(:group, parent: common_ancestor) }
|
||||
let_it_be(:group) { create(:group, parent: common_ancestor) }
|
||||
|
||||
context 'querying under the common ancestor' do
|
||||
it { expect(finder.execute).to be_empty }
|
||||
end
|
||||
|
||||
context 'with common ancestor' do
|
||||
let_it_be(:common_ancestor) { create(:group) }
|
||||
let_it_be(:other_group) { create(:group, parent: common_ancestor) }
|
||||
let_it_be(:group) { create(:group, parent: common_ancestor) }
|
||||
|
||||
context 'querying under the common ancestor' do
|
||||
it { expect(finder.execute).to be_empty }
|
||||
context 'querying the common ancestor' do
|
||||
subject(:finder) do
|
||||
described_class.new(current_user: user, parent_group: common_ancestor, params: params)
|
||||
end
|
||||
|
||||
context 'querying the common ancestor' do
|
||||
subject(:finder) do
|
||||
described_class.new(current_user: user, parent_group: common_ancestor, params: params)
|
||||
end
|
||||
|
||||
it 'contains shared subgroups' do
|
||||
expect(finder.execute).to contain_exactly(group, other_group)
|
||||
end
|
||||
it 'contains shared subgroups' do
|
||||
expect(finder.execute).to contain_exactly(group, other_group)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with nested groups' do
|
||||
let!(:project) { create(:project, namespace: group) }
|
||||
let!(:subgroup) { create(:group, :private, parent: group) }
|
||||
context 'with nested groups' do
|
||||
let!(:project) { create(:project, namespace: group) }
|
||||
let!(:subgroup) { create(:group, :private, parent: group) }
|
||||
|
||||
describe '#execute' do
|
||||
it 'contains projects and subgroups' do
|
||||
expect(finder.execute).to contain_exactly(subgroup, project)
|
||||
describe '#execute' do
|
||||
it 'contains projects and subgroups' do
|
||||
expect(finder.execute).to contain_exactly(subgroup, project)
|
||||
end
|
||||
|
||||
it 'does not include subgroups the user does not have access to' do
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
|
||||
public_subgroup = create(:group, :public, parent: group, path: 'public-group')
|
||||
other_subgroup = create(:group, :private, parent: group, path: 'visible-private-group')
|
||||
other_user = create(:user)
|
||||
other_subgroup.add_developer(other_user)
|
||||
|
||||
finder = described_class.new(current_user: other_user, parent_group: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(public_subgroup, other_subgroup)
|
||||
end
|
||||
|
||||
it 'only includes public groups when no user is given' do
|
||||
public_subgroup = create(:group, :public, parent: group)
|
||||
_private_subgroup = create(:group, :private, parent: group)
|
||||
|
||||
finder = described_class.new(current_user: nil, parent_group: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(public_subgroup)
|
||||
end
|
||||
|
||||
context 'when archived is `true`' do
|
||||
let(:params) { { archived: 'true' } }
|
||||
|
||||
it 'includes archived projects in the count of subgroups' do
|
||||
create(:project, namespace: subgroup, archived: true)
|
||||
|
||||
expect(finder.execute.first.preloaded_project_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a filter' do
|
||||
let(:params) { { filter: 'test' } }
|
||||
|
||||
it 'contains only matching projects and subgroups' do
|
||||
matching_project = create(:project, namespace: group, name: 'Testproject')
|
||||
matching_subgroup = create(:group, name: 'testgroup', parent: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(matching_subgroup, matching_project)
|
||||
end
|
||||
|
||||
it 'does not include subgroups the user does not have access to' do
|
||||
subgroup.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
|
||||
public_subgroup = create(:group, :public, parent: group, path: 'public-group')
|
||||
other_subgroup = create(:group, :private, parent: group, path: 'visible-private-group')
|
||||
_invisible_subgroup = create(:group, :private, parent: group, name: 'test1')
|
||||
other_subgroup = create(:group, :private, parent: group, name: 'test2')
|
||||
public_subgroup = create(:group, :public, parent: group, name: 'test3')
|
||||
other_subsubgroup = create(:group, :private, parent: other_subgroup, name: 'test4')
|
||||
other_user = create(:user)
|
||||
other_subgroup.add_developer(other_user)
|
||||
|
||||
finder = described_class.new(current_user: other_user, parent_group: group)
|
||||
finder = described_class.new(current_user: other_user,
|
||||
parent_group: group,
|
||||
params: params)
|
||||
|
||||
expect(finder.execute).to contain_exactly(public_subgroup, other_subgroup)
|
||||
expect(finder.execute).to contain_exactly(other_subgroup, public_subgroup, other_subsubgroup)
|
||||
end
|
||||
|
||||
it 'only includes public groups when no user is given' do
|
||||
public_subgroup = create(:group, :public, parent: group)
|
||||
_private_subgroup = create(:group, :private, parent: group)
|
||||
context 'with matching children' do
|
||||
it 'includes a group that has a subgroup matching the query and its parent' do
|
||||
matching_subgroup = create(:group, :private, name: 'testgroup', parent: subgroup)
|
||||
|
||||
finder = described_class.new(current_user: nil, parent_group: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(public_subgroup)
|
||||
end
|
||||
|
||||
context 'when archived is `true`' do
|
||||
let(:params) { { archived: 'true' } }
|
||||
|
||||
it 'includes archived projects in the count of subgroups' do
|
||||
create(:project, namespace: subgroup, archived: true)
|
||||
|
||||
expect(finder.execute.first.preloaded_project_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a filter' do
|
||||
let(:params) { { filter: 'test' } }
|
||||
|
||||
it 'contains only matching projects and subgroups' do
|
||||
matching_project = create(:project, namespace: group, name: 'Testproject')
|
||||
matching_subgroup = create(:group, name: 'testgroup', parent: group)
|
||||
|
||||
expect(finder.execute).to contain_exactly(matching_subgroup, matching_project)
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching_subgroup)
|
||||
end
|
||||
|
||||
it 'does not include subgroups the user does not have access to' do
|
||||
_invisible_subgroup = create(:group, :private, parent: group, name: 'test1')
|
||||
other_subgroup = create(:group, :private, parent: group, name: 'test2')
|
||||
public_subgroup = create(:group, :public, parent: group, name: 'test3')
|
||||
other_subsubgroup = create(:group, :private, parent: other_subgroup, name: 'test4')
|
||||
other_user = create(:user)
|
||||
other_subgroup.add_developer(other_user)
|
||||
it 'includes the parent of a matching project' do
|
||||
matching_project = create(:project, namespace: subgroup, name: 'Testproject')
|
||||
|
||||
finder = described_class.new(current_user: other_user,
|
||||
parent_group: group,
|
||||
params: params)
|
||||
|
||||
expect(finder.execute).to contain_exactly(other_subgroup, public_subgroup, other_subsubgroup)
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching_project)
|
||||
end
|
||||
|
||||
context 'with matching children' do
|
||||
it 'includes a group that has a subgroup matching the query and its parent' do
|
||||
matching_subgroup = create(:group, :private, name: 'testgroup', parent: subgroup)
|
||||
context 'with a small page size' do
|
||||
let(:params) { { filter: 'test', per_page: 1 } }
|
||||
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching_subgroup)
|
||||
it 'contains all the ancestors of a matching subgroup regardless the page size' do
|
||||
subgroup = create(:group, :private, parent: group)
|
||||
matching = create(:group, :private, name: 'testgroup', parent: subgroup)
|
||||
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching)
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes the parent of a matching project' do
|
||||
matching_project = create(:project, namespace: subgroup, name: 'Testproject')
|
||||
it 'does not include the parent itself' do
|
||||
group.update!(name: 'test')
|
||||
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching_project)
|
||||
end
|
||||
|
||||
context 'with a small page size' do
|
||||
let(:params) { { filter: 'test', per_page: 1 } }
|
||||
|
||||
it 'contains all the ancestors of a matching subgroup regardless the page size' do
|
||||
subgroup = create(:group, :private, parent: group)
|
||||
matching = create(:group, :private, name: 'testgroup', parent: subgroup)
|
||||
|
||||
expect(finder.execute).to contain_exactly(subgroup, matching)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include the parent itself' do
|
||||
group.update!(name: 'test')
|
||||
|
||||
expect(finder.execute).not_to include(group)
|
||||
end
|
||||
expect(finder.execute).not_to include(group)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'group descentants finder examples'
|
||||
|
||||
context 'when feature flag :linear_group_descendants_finder is disabled' do
|
||||
before do
|
||||
stub_feature_flags(linear_group_descendants_finder: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'group descentants finder examples'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -342,27 +342,30 @@ describe('Base editor', () => {
|
|||
|
||||
describe('implementation', () => {
|
||||
let instance;
|
||||
beforeEach(() => {
|
||||
instance = editor.createInstance({ el: editorEl, blobPath, blobContent });
|
||||
});
|
||||
|
||||
it('correctly proxies value from the model', () => {
|
||||
instance = editor.createInstance({ el: editorEl, blobPath, blobContent });
|
||||
expect(instance.getValue()).toBe(blobContent);
|
||||
});
|
||||
|
||||
it('emits the EDITOR_READY_EVENT event after setting up the instance', () => {
|
||||
it('emits the EDITOR_READY_EVENT event passing the instance after setting it up', () => {
|
||||
jest.spyOn(monacoEditor, 'create').mockImplementation(() => {
|
||||
return {
|
||||
setModel: jest.fn(),
|
||||
onDidDispose: jest.fn(),
|
||||
layout: jest.fn(),
|
||||
dispose: jest.fn(),
|
||||
};
|
||||
});
|
||||
const eventSpy = jest.fn();
|
||||
let passedInstance;
|
||||
const eventSpy = jest.fn().mockImplementation((ev) => {
|
||||
passedInstance = ev.detail.instance;
|
||||
});
|
||||
editorEl.addEventListener(EDITOR_READY_EVENT, eventSpy);
|
||||
expect(eventSpy).not.toHaveBeenCalled();
|
||||
editor.createInstance({ el: editorEl });
|
||||
instance = editor.createInstance({ el: editorEl });
|
||||
expect(eventSpy).toHaveBeenCalled();
|
||||
expect(passedInstance).toBe(instance);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -38,14 +38,14 @@ describe('Timezone Dropdown', () => {
|
|||
const tzStr = '[UTC + 5.5] Sri Jayawardenepura';
|
||||
const tzValue = 'Asia/Colombo';
|
||||
|
||||
expect($inputEl.val()).toBe('UTC');
|
||||
expect($inputEl.val()).toBe('Etc/UTC');
|
||||
|
||||
$(`${tzListSel}:contains('${tzStr}')`, $wrapper).trigger('click');
|
||||
|
||||
const val = $inputEl.val();
|
||||
|
||||
expect(val).toBe(tzValue);
|
||||
expect(val).not.toBe('UTC');
|
||||
expect(val).not.toBe('Etc/UTC');
|
||||
});
|
||||
|
||||
it('will format data array of timezones into a list of offsets', () => {
|
||||
|
@ -67,7 +67,7 @@ describe('Timezone Dropdown', () => {
|
|||
it('will default the timezone to UTC', () => {
|
||||
const tz = $inputEl.val();
|
||||
|
||||
expect(tz).toBe('UTC');
|
||||
expect(tz).toBe('Etc/UTC');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import BlobButtonGroup from '~/repository/components/blob_button_group.vue';
|
||||
import DeleteBlobModal from '~/repository/components/delete_blob_modal.vue';
|
||||
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
|
||||
|
@ -16,6 +15,7 @@ const DEFAULT_PROPS = {
|
|||
projectPath: 'some/project/path',
|
||||
isLocked: false,
|
||||
canLock: true,
|
||||
showForkSuggestion: false,
|
||||
};
|
||||
|
||||
const DEFAULT_INJECT = {
|
||||
|
@ -27,7 +27,7 @@ describe('BlobButtonGroup component', () => {
|
|||
let wrapper;
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(BlobButtonGroup, {
|
||||
wrapper = mountExtended(BlobButtonGroup, {
|
||||
propsData: {
|
||||
...DEFAULT_PROPS,
|
||||
...props,
|
||||
|
@ -35,9 +35,6 @@ describe('BlobButtonGroup component', () => {
|
|||
provide: {
|
||||
...DEFAULT_INJECT,
|
||||
},
|
||||
directives: {
|
||||
GlModal: createMockDirective(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -47,7 +44,8 @@ describe('BlobButtonGroup component', () => {
|
|||
|
||||
const findDeleteBlobModal = () => wrapper.findComponent(DeleteBlobModal);
|
||||
const findUploadBlobModal = () => wrapper.findComponent(UploadBlobModal);
|
||||
const findReplaceButton = () => wrapper.find('[data-testid="replace"]');
|
||||
const findDeleteButton = () => wrapper.findByTestId('delete');
|
||||
const findReplaceButton = () => wrapper.findByTestId('replace');
|
||||
|
||||
it('renders component', () => {
|
||||
createComponent();
|
||||
|
@ -63,6 +61,8 @@ describe('BlobButtonGroup component', () => {
|
|||
describe('buttons', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
jest.spyOn(findUploadBlobModal().vm, 'show');
|
||||
jest.spyOn(findDeleteBlobModal().vm, 'show');
|
||||
});
|
||||
|
||||
it('renders both the replace and delete button', () => {
|
||||
|
@ -75,10 +75,37 @@ describe('BlobButtonGroup component', () => {
|
|||
});
|
||||
|
||||
it('triggers the UploadBlobModal from the replace button', () => {
|
||||
const { value } = getBinding(findReplaceButton().element, 'gl-modal');
|
||||
const modalId = findUploadBlobModal().props('modalId');
|
||||
findReplaceButton().trigger('click');
|
||||
|
||||
expect(modalId).toEqual(value);
|
||||
expect(findUploadBlobModal().vm.show).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers the DeleteBlobModal from the delete button', () => {
|
||||
findDeleteButton().trigger('click');
|
||||
|
||||
expect(findDeleteBlobModal().vm.show).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('showForkSuggestion set to true', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ showForkSuggestion: true });
|
||||
jest.spyOn(findUploadBlobModal().vm, 'show');
|
||||
jest.spyOn(findDeleteBlobModal().vm, 'show');
|
||||
});
|
||||
|
||||
it('does not trigger the UploadBlobModal from the replace button', () => {
|
||||
findReplaceButton().trigger('click');
|
||||
|
||||
expect(findUploadBlobModal().vm.show).not.toHaveBeenCalled();
|
||||
expect(wrapper.emitted().fork).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not trigger the DeleteBlobModal from the delete button', () => {
|
||||
findDeleteButton().trigger('click');
|
||||
|
||||
expect(findDeleteBlobModal().vm.show).not.toHaveBeenCalled();
|
||||
expect(wrapper.emitted().fork).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -56,52 +56,6 @@ RSpec.describe Banzai::Filter::FootnoteFilter do
|
|||
it 'properly adds the necessary ids and classes' do
|
||||
expect(doc.to_html).to eq filtered_footnote.strip
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
# first[^1] and second[^second]
|
||||
# [^1]: one
|
||||
# [^second]: two
|
||||
let(:footnote) do
|
||||
<<~EOF
|
||||
<p>first<sup><a href="#fn1" id="fnref1">1</a></sup> and second<sup><a href="#fn2" id="fnref2">2</a></sup></p>
|
||||
<p>same reference<sup><a href="#fn1" id="fnref1">1</a></sup></p>
|
||||
<ol>
|
||||
<li id="fn1">
|
||||
<p>one <a href="#fnref1">↩</a></p>
|
||||
</li>
|
||||
<li id="fn2">
|
||||
<p>two <a href="#fnref2">↩</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
EOF
|
||||
end
|
||||
|
||||
let(:filtered_footnote) do
|
||||
<<~EOF
|
||||
<p>first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup></p>
|
||||
<p>same reference<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup></p>
|
||||
<section class="footnotes"><ol>
|
||||
<li id="fn1-#{identifier}">
|
||||
<p>one <a href="#fnref1-#{identifier}" class="footnote-backref">↩</a></p>
|
||||
</li>
|
||||
<li id="fn2-#{identifier}">
|
||||
<p>two <a href="#fnref2-#{identifier}" class="footnote-backref">↩</a></p>
|
||||
</li>
|
||||
</ol></section>
|
||||
EOF
|
||||
end
|
||||
|
||||
let(:doc) { filter(footnote) }
|
||||
let(:identifier) { link_node[:id].delete_prefix('fnref1-') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
|
||||
it 'properly adds the necessary ids and classes' do
|
||||
expect(doc.to_html).to eq filtered_footnote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when detecting footnotes' do
|
||||
|
|
|
@ -5,125 +5,90 @@ require 'spec_helper'
|
|||
RSpec.describe Banzai::Filter::MarkdownFilter do
|
||||
include FilterSpecHelper
|
||||
|
||||
shared_examples_for 'renders correct markdown' do
|
||||
describe 'markdown engine from context' do
|
||||
it 'defaults to CommonMark' do
|
||||
expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
|
||||
expect(instance).to receive(:render).and_return('test')
|
||||
end
|
||||
|
||||
filter('test')
|
||||
describe 'markdown engine from context' do
|
||||
it 'defaults to CommonMark' do
|
||||
expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
|
||||
expect(instance).to receive(:render).and_return('test')
|
||||
end
|
||||
|
||||
it 'uses CommonMark' do
|
||||
expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
|
||||
expect(instance).to receive(:render).and_return('test')
|
||||
end
|
||||
|
||||
filter('test', { markdown_engine: :common_mark })
|
||||
end
|
||||
filter('test')
|
||||
end
|
||||
|
||||
describe 'code block' do
|
||||
context 'using CommonMark' do
|
||||
before do
|
||||
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
|
||||
end
|
||||
|
||||
it 'adds language to lang attribute when specified' do
|
||||
result = filter("```html\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
expect(result).to start_with('<pre lang="html"><code>')
|
||||
else
|
||||
expect(result).to start_with('<pre><code lang="html">')
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not add language to lang attribute when not specified' do
|
||||
result = filter("```\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
expect(result).to start_with('<pre><code>')
|
||||
end
|
||||
|
||||
it 'works with utf8 chars in language' do
|
||||
result = filter("```日\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
expect(result).to start_with('<pre lang="日"><code>')
|
||||
else
|
||||
expect(result).to start_with('<pre><code lang="日">')
|
||||
end
|
||||
end
|
||||
|
||||
it 'works with additional language parameters' do
|
||||
result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
expect(result).to start_with('<pre lang="ruby:red" data-meta="gem foo"><code>')
|
||||
else
|
||||
expect(result).to start_with('<pre><code lang="ruby:red gem foo">')
|
||||
end
|
||||
end
|
||||
it 'uses CommonMark' do
|
||||
expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
|
||||
expect(instance).to receive(:render).and_return('test')
|
||||
end
|
||||
|
||||
filter('test', { markdown_engine: :common_mark })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'source line position' do
|
||||
context 'using CommonMark' do
|
||||
before do
|
||||
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
|
||||
end
|
||||
|
||||
it 'defaults to add data-sourcepos' do
|
||||
result = filter('test')
|
||||
|
||||
expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>'
|
||||
end
|
||||
|
||||
it 'disables data-sourcepos' do
|
||||
result = filter('test', no_sourcepos: true)
|
||||
|
||||
expect(result).to eq '<p>test</p>'
|
||||
end
|
||||
describe 'code block' do
|
||||
context 'using CommonMark' do
|
||||
before do
|
||||
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'footnotes in tables' do
|
||||
it 'processes footnotes in table cells' do
|
||||
text = <<-MD.strip_heredoc
|
||||
| Column1 |
|
||||
| --------- |
|
||||
| foot [^1] |
|
||||
it 'adds language to lang attribute when specified' do
|
||||
result = filter("```html\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
[^1]: a footnote
|
||||
MD
|
||||
expect(result).to start_with('<pre lang="html"><code>')
|
||||
end
|
||||
|
||||
result = filter(text, no_sourcepos: true)
|
||||
it 'does not add language to lang attribute when not specified' do
|
||||
result = filter("```\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
expect(result).to include('<td>foot <sup')
|
||||
expect(result).to start_with('<pre><code>')
|
||||
end
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
expect(result).to include('<section class="footnotes" data-footnotes>')
|
||||
else
|
||||
expect(result).to include('<section class="footnotes">')
|
||||
end
|
||||
it 'works with utf8 chars in language' do
|
||||
result = filter("```日\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
expect(result).to start_with('<pre lang="日"><code>')
|
||||
end
|
||||
|
||||
it 'works with additional language parameters' do
|
||||
result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true)
|
||||
|
||||
expect(result).to start_with('<pre lang="ruby:red" data-meta="gem foo"><code>')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
describe 'source line position' do
|
||||
context 'using CommonMark' do
|
||||
before do
|
||||
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
|
||||
end
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
it 'defaults to add data-sourcepos' do
|
||||
result = filter('test')
|
||||
|
||||
expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>'
|
||||
end
|
||||
|
||||
it 'disables data-sourcepos' do
|
||||
result = filter('test', no_sourcepos: true)
|
||||
|
||||
expect(result).to eq '<p>test</p>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'using c-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: true)
|
||||
end
|
||||
describe 'footnotes in tables' do
|
||||
it 'processes footnotes in table cells' do
|
||||
text = <<-MD.strip_heredoc
|
||||
| Column1 |
|
||||
| --------- |
|
||||
| foot [^1] |
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
[^1]: a footnote
|
||||
MD
|
||||
|
||||
result = filter(text, no_sourcepos: true)
|
||||
|
||||
expect(result).to include('<td>foot <sup')
|
||||
expect(result).to include('<section class="footnotes" data-footnotes>')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,67 +5,33 @@ require 'spec_helper'
|
|||
RSpec.describe Banzai::Filter::PlantumlFilter do
|
||||
include FilterSpecHelper
|
||||
|
||||
shared_examples_for 'renders correct markdown' do
|
||||
it 'replaces plantuml pre tag with img tag' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
it 'replaces plantuml pre tag with img tag' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
|
||||
input = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
'<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
else
|
||||
'<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>'
|
||||
end
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>'
|
||||
doc = filter(input)
|
||||
|
||||
output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
|
||||
it 'does not replace plantuml pre tag with img tag if disabled' do
|
||||
stub_application_setting(plantuml_enabled: false)
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
else
|
||||
input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>'
|
||||
output = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>'
|
||||
end
|
||||
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
|
||||
it 'does not replace plantuml pre tag with img tag if url is invalid' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid")
|
||||
|
||||
input = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
'<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
else
|
||||
'<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>'
|
||||
end
|
||||
|
||||
output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
it 'does not replace plantuml pre tag with img tag if disabled' do
|
||||
stub_application_setting(plantuml_enabled: false)
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
|
||||
context 'using c-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: true)
|
||||
end
|
||||
it 'does not replace plantuml pre tag with img tag if url is invalid' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid")
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
end
|
||||
end
|
||||
|
|
|
@ -177,53 +177,6 @@ RSpec.describe Banzai::Filter::SanitizationFilter do
|
|||
expect(act.to_html).to eq exp
|
||||
end
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
|
||||
it 'allows correct footnote id property on links' do
|
||||
exp = %q(<a href="#fn1" id="fnref1">foo/bar.md</a>)
|
||||
act = filter(exp)
|
||||
|
||||
expect(act.to_html).to eq exp
|
||||
end
|
||||
|
||||
it 'allows correct footnote id property on li element' do
|
||||
exp = %q(<ol><li id="fn1">footnote</li></ol>)
|
||||
act = filter(exp)
|
||||
|
||||
expect(act.to_html).to eq exp
|
||||
end
|
||||
|
||||
it 'removes invalid id for footnote links' do
|
||||
exp = %q(<a href="#fn1">link</a>)
|
||||
|
||||
%w[fnrefx test xfnref1].each do |id|
|
||||
act = filter(%(<a href="#fn1" id="#{id}">link</a>))
|
||||
|
||||
expect(act.to_html).to eq exp
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes invalid id for footnote li' do
|
||||
exp = %q(<ol><li>footnote</li></ol>)
|
||||
|
||||
%w[fnx test xfn1].each do |id|
|
||||
act = filter(%(<ol><li id="#{id}">footnote</li></ol>))
|
||||
|
||||
expect(act.to_html).to eq exp
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows footnotes numbered higher than 9' do
|
||||
exp = %q(<a href="#fn15" id="fnref15">link</a><ol><li id="fn15">footnote</li></ol>)
|
||||
act = filter(exp)
|
||||
|
||||
expect(act.to_html).to eq exp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,202 +19,142 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'renders correct markdown' do
|
||||
context "when no language is specified" do
|
||||
it "highlights as plaintext" do
|
||||
result = filter('<pre><code>def fun end</code></pre>')
|
||||
context "when no language is specified" do
|
||||
it "highlights as plaintext" do
|
||||
result = filter('<pre><code>def fun end</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", ""
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
context "when contains mermaid diagrams" do
|
||||
it "ignores mermaid blocks" do
|
||||
result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>')
|
||||
include_examples "XSS prevention", ""
|
||||
end
|
||||
|
||||
expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>')
|
||||
end
|
||||
end
|
||||
context "when contains mermaid diagrams" do
|
||||
it "ignores mermaid blocks" do
|
||||
result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>')
|
||||
|
||||
context "when a valid language is specified" do
|
||||
it "highlights as that language" do
|
||||
result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
filter('<pre lang="ruby"><code>def fun end</code></pre>')
|
||||
else
|
||||
filter('<pre><code lang="ruby">def fun end</code></pre>')
|
||||
end
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "ruby"
|
||||
end
|
||||
|
||||
context "when an invalid language is specified" do
|
||||
it "highlights as plaintext" do
|
||||
result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
filter('<pre lang="gnuplot"><code>This is a test</code></pre>')
|
||||
else
|
||||
filter('<pre><code lang="gnuplot">This is a test</code></pre>')
|
||||
end
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "gnuplot"
|
||||
end
|
||||
|
||||
context "languages that should be passed through" do
|
||||
let(:delimiter) { described_class::LANG_PARAMS_DELIMITER }
|
||||
let(:data_attr) { described_class::LANG_PARAMS_ATTR }
|
||||
|
||||
%w(math mermaid plantuml suggestion).each do |lang|
|
||||
context "when #{lang} is specified" do
|
||||
it "highlights as plaintext but with the correct language attribute and class" do
|
||||
result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
filter(%{<pre lang="#{lang}"><code>This is a test</code></pre>})
|
||||
else
|
||||
filter(%{<pre><code lang="#{lang}">This is a test</code></pre>})
|
||||
end
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", lang
|
||||
end
|
||||
|
||||
context "when #{lang} has extra params" do
|
||||
let(:lang_params) { 'foo-bar-kux' }
|
||||
|
||||
let(:xss_lang) do
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
|
||||
else
|
||||
"#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>"
|
||||
end
|
||||
end
|
||||
|
||||
it "includes data-lang-params tag with extra information" do
|
||||
result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
filter(%{<pre lang="#{lang}" data-meta="#{lang_params}"><code>This is a test</code></pre>})
|
||||
else
|
||||
filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}">This is a test</code></pre>})
|
||||
end
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", lang
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
include_examples "XSS prevention",
|
||||
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
|
||||
else
|
||||
include_examples "XSS prevention",
|
||||
"#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>"
|
||||
end
|
||||
|
||||
include_examples "XSS prevention",
|
||||
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple param delimiters are used' do
|
||||
let(:lang) { 'suggestion' }
|
||||
let(:lang_params) { '-1+10' }
|
||||
|
||||
let(:expected_result) do
|
||||
%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params} more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}
|
||||
end
|
||||
|
||||
context 'when delimiter is space' do
|
||||
it 'delimits on the first appearance' do
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params} more-things"><code>This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(expected_result)
|
||||
else
|
||||
result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}#{delimiter}more-things">This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}#{delimiter}more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when delimiter is colon' do
|
||||
it 'delimits on the first appearance' do
|
||||
result = filter(%{<pre lang="#{lang}#{delimiter}#{lang_params} more-things"><code>This is a test</code></pre>})
|
||||
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
expect(result.to_html.delete("\n")).to eq(expected_result)
|
||||
else
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class=\"code highlight js-syntax-highlight language-plaintext\" lang=\"plaintext\" v-pre=\"true\"><code><span id=\"LC1\" class=\"line\" lang=\"plaintext\">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when sourcepos metadata is available" do
|
||||
it "includes it in the highlighted code block" do
|
||||
result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
end
|
||||
|
||||
context "when Rouge lexing fails" do
|
||||
before do
|
||||
allow_next_instance_of(Rouge::Lexers::Ruby) do |instance|
|
||||
allow(instance).to receive(:stream_tokens).and_raise(StandardError)
|
||||
end
|
||||
end
|
||||
|
||||
it "highlights as plaintext" do
|
||||
result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
filter('<pre lang="ruby"><code>This is a test</code></pre>')
|
||||
else
|
||||
filter('<pre><code lang="ruby">This is a test</code></pre>')
|
||||
end
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "ruby"
|
||||
end
|
||||
|
||||
context "when Rouge lexing fails after a retry" do
|
||||
before do
|
||||
allow_next_instance_of(Rouge::Lexers::PlainText) do |instance|
|
||||
allow(instance).to receive(:stream_tokens).and_raise(StandardError)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not add highlighting classes" do
|
||||
result = filter('<pre><code>This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html).to eq('<pre><code>This is a test</code></pre>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "ruby"
|
||||
expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>')
|
||||
end
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
context "when a valid language is specified" do
|
||||
it "highlights as that language" do
|
||||
result = filter('<pre lang="ruby"><code>def fun end</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
include_examples "XSS prevention", "ruby"
|
||||
end
|
||||
|
||||
context 'using c-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: true)
|
||||
context "when an invalid language is specified" do
|
||||
it "highlights as plaintext" do
|
||||
result = filter('<pre lang="gnuplot"><code>This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
include_examples "XSS prevention", "gnuplot"
|
||||
end
|
||||
|
||||
context "languages that should be passed through" do
|
||||
let(:delimiter) { described_class::LANG_PARAMS_DELIMITER }
|
||||
let(:data_attr) { described_class::LANG_PARAMS_ATTR }
|
||||
|
||||
%w(math mermaid plantuml suggestion).each do |lang|
|
||||
context "when #{lang} is specified" do
|
||||
it "highlights as plaintext but with the correct language attribute and class" do
|
||||
result = filter(%{<pre lang="#{lang}"><code>This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", lang
|
||||
end
|
||||
|
||||
context "when #{lang} has extra params" do
|
||||
let(:lang_params) { 'foo-bar-kux' }
|
||||
let(:xss_lang) { "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" }
|
||||
|
||||
it "includes data-lang-params tag with extra information" do
|
||||
result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params}"><code>This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>})
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", lang
|
||||
|
||||
include_examples "XSS prevention",
|
||||
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
|
||||
|
||||
include_examples "XSS prevention",
|
||||
"#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple param delimiters are used' do
|
||||
let(:lang) { 'suggestion' }
|
||||
let(:lang_params) { '-1+10' }
|
||||
|
||||
let(:expected_result) do
|
||||
%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params} more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}
|
||||
end
|
||||
|
||||
context 'when delimiter is space' do
|
||||
it 'delimits on the first appearance' do
|
||||
result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params} more-things"><code>This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(expected_result)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when delimiter is colon' do
|
||||
it 'delimits on the first appearance' do
|
||||
result = filter(%{<pre lang="#{lang}#{delimiter}#{lang_params} more-things"><code>This is a test</code></pre>})
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq(expected_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when sourcepos metadata is available" do
|
||||
it "includes it in the highlighted code block" do
|
||||
result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
end
|
||||
|
||||
context "when Rouge lexing fails" do
|
||||
before do
|
||||
allow_next_instance_of(Rouge::Lexers::Ruby) do |instance|
|
||||
allow(instance).to receive(:stream_tokens).and_raise(StandardError)
|
||||
end
|
||||
end
|
||||
|
||||
it "highlights as plaintext" do
|
||||
result = filter('<pre lang="ruby"><code>This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre><copy-code></copy-code></div>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "ruby"
|
||||
end
|
||||
|
||||
context "when Rouge lexing fails after a retry" do
|
||||
before do
|
||||
allow_next_instance_of(Rouge::Lexers::PlainText) do |instance|
|
||||
allow(instance).to receive(:stream_tokens).and_raise(StandardError)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not add highlighting classes" do
|
||||
result = filter('<pre><code>This is a test</code></pre>')
|
||||
|
||||
expect(result.to_html).to eq('<pre><code>This is a test</code></pre>')
|
||||
end
|
||||
|
||||
include_examples "XSS prevention", "ruby"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,47 +65,6 @@ RSpec.describe Banzai::Pipeline::FullPipeline do
|
|||
|
||||
expect(html.lines.map(&:strip).join("\n")).to eq filtered_footnote.strip
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
let(:html) { described_class.to_html(footnote_markdown, project: project) }
|
||||
let(:identifier) { html[/.*fnref1-(\d+).*/, 1] }
|
||||
let(:footnote_markdown) do
|
||||
<<~EOF
|
||||
first[^1] and second[^second] and twenty[^twenty]
|
||||
[^1]: one
|
||||
[^second]: two
|
||||
[^twenty]: twenty
|
||||
EOF
|
||||
end
|
||||
|
||||
let(:filtered_footnote) do
|
||||
<<~EOF
|
||||
<p dir="auto">first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup> and twenty<sup class="footnote-ref"><a href="#fn3-#{identifier}" id="fnref3-#{identifier}">3</a></sup></p>
|
||||
|
||||
<section class="footnotes"><ol>
|
||||
<li id="fn1-#{identifier}">
|
||||
<p>one <a href="#fnref1-#{identifier}" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
</li>
|
||||
<li id="fn2-#{identifier}">
|
||||
<p>two <a href="#fnref2-#{identifier}" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
</li>
|
||||
<li id="fn3-#{identifier}">
|
||||
<p>twenty <a href="#fnref3-#{identifier}" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
</li>
|
||||
</ol></section>
|
||||
EOF
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
|
||||
it 'properly adds the necessary ids and classes' do
|
||||
stub_commonmark_sourcepos_disabled
|
||||
|
||||
expect(html.lines.map(&:strip).join("\n")).to eq filtered_footnote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'links are detected as malicious' do
|
||||
|
|
|
@ -5,117 +5,93 @@ require 'spec_helper'
|
|||
RSpec.describe Banzai::Pipeline::PlainMarkdownPipeline do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
shared_examples_for 'renders correct markdown' do
|
||||
describe 'CommonMark tests', :aggregate_failures do
|
||||
it 'converts all reference punctuation to literals' do
|
||||
reference_chars = Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS
|
||||
markdown = reference_chars.split('').map {|char| char.prepend("\\") }.join
|
||||
punctuation = Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS.split('')
|
||||
punctuation = punctuation.delete_if {|char| char == '&' }
|
||||
punctuation << '&'
|
||||
|
||||
result = described_class.call(markdown, project: project)
|
||||
output = result[:output].to_html
|
||||
|
||||
punctuation.each { |char| expect(output).to include("<span>#{char}</span>") }
|
||||
expect(result[:escaped_literals]).to be_truthy
|
||||
end
|
||||
|
||||
it 'ensure we handle all the GitLab reference characters', :eager_load do
|
||||
reference_chars = ObjectSpace.each_object(Class).map do |klass|
|
||||
next unless klass.included_modules.include?(Referable)
|
||||
next unless klass.respond_to?(:reference_prefix)
|
||||
next unless klass.reference_prefix.length == 1
|
||||
|
||||
klass.reference_prefix
|
||||
end.compact
|
||||
|
||||
reference_chars.all? do |char|
|
||||
Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS.include?(char)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not convert non-reference punctuation to spans' do
|
||||
markdown = %q(\"\'\*\+\,\-\.\/\:\;\<\=\>\?\[\]\_\`\{\|\}) + %q[\(\)\\\\]
|
||||
|
||||
result = described_class.call(markdown, project: project)
|
||||
output = result[:output].to_html
|
||||
|
||||
expect(output).not_to include('<span>')
|
||||
expect(result[:escaped_literals]).to be_falsey
|
||||
end
|
||||
|
||||
it 'does not convert other characters to literals' do
|
||||
markdown = %q(\→\A\a\ \3\φ\«)
|
||||
expected = '\→\A\a\ \3\φ\«'
|
||||
|
||||
result = correct_html_included(markdown, expected)
|
||||
expect(result[:escaped_literals]).to be_falsey
|
||||
end
|
||||
|
||||
describe 'backslash escapes do not work in code blocks, code spans, autolinks, or raw HTML' do
|
||||
where(:markdown, :expected) do
|
||||
%q(`` \@\! ``) | %q(<code>\@\!</code>)
|
||||
%q( \@\!) | %Q(<code>\\@\\!\n</code>)
|
||||
%Q(~~~\n\\@\\!\n~~~) | %Q(<code>\\@\\!\n</code>)
|
||||
%q(<http://example.com?find=\@>) | %q(<a href="http://example.com?find=%5C@">http://example.com?find=\@</a>)
|
||||
%q[<a href="/bar\@)">] | %q[<a href="/bar%5C@)">]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { correct_html_included(markdown, expected) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'work in all other contexts, including URLs and link titles, link references, and info strings in fenced code blocks' do
|
||||
let(:markdown) { %Q(``` foo\\@bar\nfoo\n```) }
|
||||
|
||||
it 'renders correct html' do
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
correct_html_included(markdown, %Q(<pre data-sourcepos="1:1-3:3" lang="foo@bar"><code>foo\n</code></pre>))
|
||||
else
|
||||
correct_html_included(markdown, %Q(<code lang="foo@bar">foo\n</code>))
|
||||
end
|
||||
end
|
||||
|
||||
where(:markdown, :expected) do
|
||||
%q![foo](/bar\@ "\@title")! | %q(<a href="/bar@" title="@title">foo</a>)
|
||||
%Q![foo]\n\n[foo]: /bar\\@ "\\@title"! | %q(<a href="/bar@" title="@title">foo</a>)
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { correct_html_included(markdown, expected) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'backslash escapes' do
|
||||
describe 'backslash escapes', :aggregate_failures do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
|
||||
def correct_html_included(markdown, expected)
|
||||
result = described_class.call(markdown, {})
|
||||
it 'converts all reference punctuation to literals' do
|
||||
reference_chars = Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS
|
||||
markdown = reference_chars.split('').map {|char| char.prepend("\\") }.join
|
||||
punctuation = Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS.split('')
|
||||
punctuation = punctuation.delete_if {|char| char == '&' }
|
||||
punctuation << '&'
|
||||
|
||||
expect(result[:output].to_html).to include(expected)
|
||||
result = described_class.call(markdown, project: project)
|
||||
output = result[:output].to_html
|
||||
|
||||
result
|
||||
punctuation.each { |char| expect(output).to include("<span>#{char}</span>") }
|
||||
expect(result[:escaped_literals]).to be_truthy
|
||||
end
|
||||
|
||||
context 'using ruby-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: false)
|
||||
end
|
||||
it 'ensure we handle all the GitLab reference characters', :eager_load do
|
||||
reference_chars = ObjectSpace.each_object(Class).map do |klass|
|
||||
next unless klass.included_modules.include?(Referable)
|
||||
next unless klass.respond_to?(:reference_prefix)
|
||||
next unless klass.reference_prefix.length == 1
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
klass.reference_prefix
|
||||
end.compact
|
||||
|
||||
reference_chars.all? do |char|
|
||||
Banzai::Filter::MarkdownPreEscapeFilter::REFERENCE_CHARACTERS.include?(char)
|
||||
end
|
||||
end
|
||||
|
||||
context 'using c-based HTML renderer' do
|
||||
before do
|
||||
stub_feature_flags(use_cmark_renderer: true)
|
||||
it 'does not convert non-reference punctuation to spans' do
|
||||
markdown = %q(\"\'\*\+\,\-\.\/\:\;\<\=\>\?\[\]\_\`\{\|\}) + %q[\(\)\\\\]
|
||||
|
||||
result = described_class.call(markdown, project: project)
|
||||
output = result[:output].to_html
|
||||
|
||||
expect(output).not_to include('<span>')
|
||||
expect(result[:escaped_literals]).to be_falsey
|
||||
end
|
||||
|
||||
it 'does not convert other characters to literals' do
|
||||
markdown = %q(\→\A\a\ \3\φ\«)
|
||||
expected = '\→\A\a\ \3\φ\«'
|
||||
|
||||
result = correct_html_included(markdown, expected)
|
||||
expect(result[:escaped_literals]).to be_falsey
|
||||
end
|
||||
|
||||
describe 'backslash escapes do not work in code blocks, code spans, autolinks, or raw HTML' do
|
||||
where(:markdown, :expected) do
|
||||
%q(`` \@\! ``) | %q(<code>\@\!</code>)
|
||||
%q( \@\!) | %Q(<code>\\@\\!\n</code>)
|
||||
%Q(~~~\n\\@\\!\n~~~) | %Q(<code>\\@\\!\n</code>)
|
||||
%q(<http://example.com?find=\@>) | %q(<a href="http://example.com?find=%5C@">http://example.com?find=\@</a>)
|
||||
%q[<a href="/bar\@)">] | %q[<a href="/bar%5C@)">]
|
||||
end
|
||||
|
||||
it_behaves_like 'renders correct markdown'
|
||||
with_them do
|
||||
it { correct_html_included(markdown, expected) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'work in all other contexts, including URLs and link titles, link references, and info strings in fenced code blocks' do
|
||||
let(:markdown) { %Q(``` foo\\@bar\nfoo\n```) }
|
||||
|
||||
it 'renders correct html' do
|
||||
correct_html_included(markdown, %Q(<pre data-sourcepos="1:1-3:3" lang="foo@bar"><code>foo\n</code></pre>))
|
||||
end
|
||||
|
||||
where(:markdown, :expected) do
|
||||
%q![foo](/bar\@ "\@title")! | %q(<a href="/bar@" title="@title">foo</a>)
|
||||
%Q![foo]\n\n[foo]: /bar\\@ "\\@title"! | %q(<a href="/bar@" title="@title">foo</a>)
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { correct_html_included(markdown, expected) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def correct_html_included(markdown, expected)
|
||||
result = described_class.call(markdown, {})
|
||||
|
||||
expect(result[:output].to_html).to include(expected)
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -292,45 +292,84 @@ RSpec.describe Gitlab::MergeRequests::CommitMessageGenerator do
|
|||
context 'when project has merge commit template with approvers' do
|
||||
let(:user1) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(message_template_name) do
|
||||
"Merge Request approved by:\n%{approved_by}"
|
||||
end
|
||||
let(message_template_name) { <<~MSG.rstrip }
|
||||
Merge branch '%{source_branch}' into '%{target_branch}'
|
||||
|
||||
context "and mr has no approval" do
|
||||
%{approved_by}
|
||||
MSG
|
||||
|
||||
context 'and mr has no approval' do
|
||||
before do
|
||||
merge_request.approved_by_users = []
|
||||
end
|
||||
|
||||
it "returns empty string" do
|
||||
it 'removes variable and blank line' do
|
||||
expect(result_message).to eq <<~MSG.rstrip
|
||||
Merge Request approved by:
|
||||
Merge branch 'feature' into 'master'
|
||||
MSG
|
||||
end
|
||||
|
||||
context 'when there is blank line after approved_by' do
|
||||
let(message_template_name) { <<~MSG.rstrip }
|
||||
Merge branch '%{source_branch}' into '%{target_branch}'
|
||||
|
||||
%{approved_by}
|
||||
|
||||
Type: merge
|
||||
MSG
|
||||
|
||||
it 'removes blank line before it' do
|
||||
expect(result_message).to eq <<~MSG.rstrip
|
||||
Merge branch 'feature' into 'master'
|
||||
|
||||
Type: merge
|
||||
MSG
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no blank line after approved_by' do
|
||||
let(message_template_name) { <<~MSG.rstrip }
|
||||
Merge branch '%{source_branch}' into '%{target_branch}'
|
||||
|
||||
%{approved_by}
|
||||
Type: merge
|
||||
MSG
|
||||
|
||||
it 'does not remove blank line before it' do
|
||||
expect(result_message).to eq <<~MSG.rstrip
|
||||
Merge branch 'feature' into 'master'
|
||||
|
||||
Type: merge
|
||||
MSG
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "and mr has one approval" do
|
||||
context 'and mr has one approval' do
|
||||
before do
|
||||
merge_request.approved_by_users = [user1]
|
||||
end
|
||||
|
||||
it "returns user name and email" do
|
||||
it 'returns user name and email' do
|
||||
expect(result_message).to eq <<~MSG.rstrip
|
||||
Merge Request approved by:
|
||||
Approved-by: #{user1.name} <#{user1.email}>
|
||||
Merge branch 'feature' into 'master'
|
||||
|
||||
Approved-by: #{user1.name} <#{user1.email}>
|
||||
MSG
|
||||
end
|
||||
end
|
||||
|
||||
context "and mr has multiple approvals" do
|
||||
context 'and mr has multiple approvals' do
|
||||
before do
|
||||
merge_request.approved_by_users = [user1, user2]
|
||||
end
|
||||
|
||||
it "returns users names and emails" do
|
||||
it 'returns users names and emails' do
|
||||
expect(result_message).to eq <<~MSG.rstrip
|
||||
Merge Request approved by:
|
||||
Approved-by: #{user1.name} <#{user1.email}>
|
||||
Approved-by: #{user2.name} <#{user2.email}>
|
||||
Merge branch 'feature' into 'master'
|
||||
|
||||
Approved-by: #{user1.name} <#{user1.email}>
|
||||
Approved-by: #{user2.name} <#{user2.email}>
|
||||
MSG
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,12 +92,7 @@ module StubGitlabCalls
|
|||
end
|
||||
|
||||
def stub_commonmark_sourcepos_disabled
|
||||
render_options =
|
||||
if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml)
|
||||
Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_C
|
||||
else
|
||||
Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_RUBY
|
||||
end
|
||||
render_options = Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS
|
||||
|
||||
allow_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark)
|
||||
.to receive(:render_options)
|
||||
|
|
Loading…
Reference in a new issue