Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-11-20 00:14:01 +00:00
parent 652d8b33ee
commit d6085b68c5
18 changed files with 1058 additions and 408 deletions

View file

@ -1 +1 @@
b6dda5d1f7a7e05c34ed0f72f161a46aee536d75
6715e04b0a20714776227eaa67d68a6fa1ccfbdf

View file

@ -73,7 +73,7 @@ These features can also help with compliance requirements:
- [**Generate reports on permission levels of users**](../user/admin_area/index.md#user-permission-export) (for
instances): Administrators can generate a report listing all users' access permissions for groups and projects in the
instance.
- [**Lock project membership to group**](../user/group/index.md#prevent-members-from-being-added-to-a-group) (for
- [**Lock project membership to group**](../user/group/index.md#prevent-members-from-being-added-to-projects-in-a-group) (for
groups): Group owners can prevent new members from being added to projects within a group.
- [**LDAP group sync**](auth/ldap/ldap_synchronization.md#group-sync) (for instances): Gives administrators the ability
to automatically sync groups and manage SSH keys, permissions, and authentication, so you can focus on building your

View file

@ -788,7 +788,7 @@ Parameters:
| `name` | string | yes | The name of the group. |
| `path` | string | yes | The path of the group. |
| `description` | string | no | The group's description. |
| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to project membership within this group. |
| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to projects within this group. |
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
@ -864,7 +864,7 @@ PUT /groups/:id
| `name` | string | no | The name of the group. |
| `path` | string | no | The path of the group. |
| `description` | string | no | The description of the group. |
| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to project membership within this group. |
| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to projects within this group. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |

View file

@ -381,7 +381,7 @@ first time.
### Requesting a review
When you are ready to have your merge request reviewed,
you should request an initial review by assigning it to a reviewer from your group or team.
you should [request an initial review](../user/project/merge_requests/getting_started.md#reviewer) by selecting a reviewer from your group or team.
However, you can also assign it to any reviewer. The list of reviewers can be found on [Engineering projects](https://about.gitlab.com/handbook/engineering/projects/) page.
You can also use `workflow::ready for review` label. That means that your merge request is ready to be reviewed and any reviewer can pick it. It is recommended to use that label only if there isn't time pressure and make sure the merge request is assigned to a reviewer.

View file

@ -28,7 +28,7 @@ the tiers are no longer mentioned in GitLab documentation:
- [Creating group memberships via CN](../user/group/index.md#create-group-links-via-cn)
- [Group push rules](../user/group/index.md#group-push-rules)
- [Managing group memberships via LDAP](../user/group/index.md#manage-group-memberships-via-ldap)
- [Member locking](../user/group/index.md#prevent-members-from-being-added-to-a-group)
- [Member locking](../user/group/index.md#prevent-members-from-being-added-to-projects-in-a-group)
- [Overriding user permissions](../user/group/index.md#override-user-permissions)
- [User contribution analytics](../user/group/contribution_analytics/index.md)
- [Kerberos integration](../integration/kerberos.md)

View file

@ -508,7 +508,7 @@ To prevent a project from being shared with other groups:
This setting applies to all subgroups unless overridden by a group owner. Groups already
added to a project lose access when the setting is enabled.
## Prevent members from being added to a group **(PREMIUM)**
## Prevent members from being added to projects in a group **(PREMIUM)**
As a group owner, you can prevent any new project membership for all
projects in a group, allowing tighter control over project membership.
@ -516,7 +516,11 @@ projects in a group, allowing tighter control over project membership.
For example, if you want to lock the group for an [Audit Event](../../administration/audit_events.md),
you can guarantee that project membership cannot be modified during the audit.
To prevent members from being added to a group:
You can still invite groups or to add members to groups, implicitly giving members access to projects in the **locked** group.
The setting does not cascade. Projects in subgroups observe the subgroup configuration, ignoring the parent group.
To prevent members from being added to projects in a group:
1. Go to the group's **Settings > General** page.
1. Expand the **Permissions, LFS, 2FA** section.

View file

@ -26247,7 +26247,7 @@ msgstr ""
msgid "Prev"
msgstr ""
msgid "Prevent adding new members to project membership within this group"
msgid "Prevent adding new members to projects within this group"
msgstr ""
msgid "Prevent auto-stopping"

View file

@ -8,11 +8,15 @@ module RuboCop
TRANSLATION_METHODS = %i[_ s_ n_].freeze
def_node_matcher :translation_method?, <<~PATTERN
(send _ _ str*)
(send _ _ str*)
PATTERN
def_node_matcher :lambda_node?, <<~PATTERN
(send _ :lambda)
(send _ :lambda)
PATTERN
def_node_matcher :struct_constant_assignment?, <<~PATTERN
(casgn _ _ `(const _ :Struct))
PATTERN
def on_send(node)
@ -27,7 +31,7 @@ module RuboCop
receiver, _ = *ancestor
break if lambda_node?(receiver) # translations defined in lambda nodes should be allowed
if constant_assignment?(ancestor)
if constant_assignment?(ancestor) && !struct_constant_assignment?(ancestor)
add_offense(node, location: :expression)
break

View file

@ -0,0 +1,792 @@
# Related Specs:
#
# This data file drives the specs in the following specs:
#
# CE Backend: spec/requests/api/markdown_golden_master_spec.rb
# CE Frontend: spec/frontend/content_editor/markdown_processing_spec.js
#
# For EE, these files are used:
# EE Data: ee/spec/fixtures/markdown/markdown_golden_master_examples.yml
# EE Backend: ee/spec/requests/api/markdown_golden_master_spec.rb
# EE Frontend: ee/spec/frontend/content_editor/markdown_processing_spec.js
#
#
# Requirements:
#
# 1. Frontend: We should have test coverage that the Content Editor can properly serialize HTML
# to Markdown for all GFM source elements which it currently supports.
# 2. Frontend: We should have test coverage that the Content Editor can properly render the expected
# HTML for all GFM source elements which it currently supports (not currently implemented in the
# frontend - this will likely be a standalone module outside of the Content Editor).
# 3. Backend: We should ensure that for all GFM elements, the backend always renders the expected
# HTML, for **all** supported GFM source elements.
#
# If any of this this ever changes unexpectedly, tests will start failing, and force the same change
# to be made on the backend and frontend.
#
#
# Overview:
#
# These specs ensure that the bidirectional Markdown <-> HTML conversion logic is implemented
# identically on the backend and frontend, for all supported GitLab-Flavored Markdown examples, by
# running hardcoded examples through the logic and ensuring the results match.
#
# This is an example of the "Golden Master Testing" approach, which is also referred to as
# "Approval Testing" or "Characterization Testing".
#
# The term "Golden Master" originally comes from the recording industry, and refers to process
# of "mastering", or making a final mix from which all other copies will be produced.
#
# See:
# - https://en.wikipedia.org/wiki/Characterization_test
# - https://en.wikipedia.org/wiki/Gold_master_(disambiguation)
#
#
# What we are doing is actually a type Golden Master testing with modifications:
#
# 1. The original markdown examples used to drive the tests are taken from this YAML, and can be
# considered a form of "fixture" in this case.
# 2. The HTML in the YAML is the "Golden Master", but we are going to use it to assert
# against **TWO** different implementations of markdown rendering:
# 1. The frontend, implemented as Jest specs.
# 1. This will assert both HTML -> markdown serialization (what it currently does), as well as...
# 2. Markdown -> HTML rendering (not currently implemented in the frontend - this will likely
# be a standalone module outside of the Content Editor)
# 1. The backend, implemented as requests specs
# 1. This will assert markdown -> HTML conversion by the backend.
#
# Also see the MR for more explanation on the details of this approach:
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68671
#
#
# Usage:
#
# * Please keep this file alphabetized.
# * To run focused example(s), set the `FOCUSED_MARKDOWN_EXAMPLES` environment variable to a
# comma-separated list of example names. This works for the frontend and backend specs.
# * Required attributes for every example:
# 1. `name`: Specifies the Name of the example, which will be printed when specs are run.
# 2. `markdown`: Specifies the Markdown for the example, which will be compared with the
# Markdown the code generates from the corresponding specified HTML.
# 3. `html`: Specifies the HTML for the example, which will be compared with the
# HTML the code generated from the corresponding specified Markdown.
# * `api_context` (optional): This is used when a single markdown can be
# rendered differently depending on the API endpoint or area of the app from which it is called or
# used. The valid values for `api_context` are: `project`, `group`, `project_wiki`,
# and (for EE only) `group_wiki`.The `name` attribute must also have a `_for_[API_CONTEXT]` suffix
# which matches the `api_context`, in order to ensure that each example has a unique `name`
# identifier. For example, `attachment_image_for_project`.
# * `pending`: To skip an example that is broken or not yet fully implemented, add
# a `pending: <reason with issue/MR URL>` attribute to the example. See
# the `a_example_of_pending` entry for an example.
# * `pending` with key: You can also mark an example pending on only the frontend or backend. See
# the `a_example_of_pending_with_keys` entry for an example.
# * `substitutions`: For examples which may have variable content in different environments,
# such as portions of the URI, or database record IDs, you can specify
# `substitutions`, which is an array of regex/replacement pairs. The HTML
# value will be normalized with each of these pairs using Ruby `gsub`
# before comparing.
# The substitution values can (and are) also reused in multiple examples
# via YAML anchors.
#
#
# Notes:
#
# * The html values should exactly match what the backend markdown API endpoints return for the
# given markdown example. The HTML is intentionally not indented, formatted, or split across lines.
# This is a bit less readable, but it makes the spec logic simpler and less error prone for edge
# cases.
#
#
# Debugging Failures and Writing New Entries:
#
# * You need to compare what is different between the expected and actual values.
# * In rspec, the diff printed out includes the full text of the HTML. This may be long, so you
# may want to turn line wrapping on or off or copy the diff to separate file(s) for easier comparison.
# * If the difference is just in an attribute value, use the `substitutions` support to normalize
# the HTML before comparing. These specs are only validating the HTML structure, the individual
# markdown elements' unit tests can provide coverage that the exact attribute values are correct.
# * If you are making a new entry, you can create the entry according to the `Usage` section above,
# but leave the `html` value blank. This will cause the spec to fail, and you can fill in the
# `html` value based on the spec failure that is printed out.
---
#- name: an_example_of_pending
# pending: 'This is an example of the pending attribute: http://example.com'
# markdown: ;)
# html: |-
# <blink data-sourcepos="1:1-1:2"/></blink>
#
#- name: an_example_of_pending_with_keys
# pending:
# frontend: 'This is an example of the frontend-only pending attribute: http://example.com'
# backend: 'This is an example of the backend-only pending attribute: http://example.com'
# markdown: ;)
# html: |-
# <blink data-sourcepos="1:1-1:2"/></blink>
- name: attachment_image_for_group
api_context: group
substitutions:
# Note: having the top level `substitutions` data structure be a hash of arrays
# allows us to compose multiple substitutions via YAML anchors (YAML anchors
# pointing to arrays can't be combined)
uri_substitution: &uri_substitution
# NOTE: We don't care about verifying specific attribute values here, that should be the
# responsibility of unit tests. These tests are about the structure of the HTML.
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
replacement: '\1\2URI_PREFIX\4'
markdown: |-
![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
html: |-
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/groups/group58/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" class="lazy gfm" data-src="/groups/group58/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
- name: attachment_image_for_project
api_context: project
substitutions:
uri_substitution: *uri_substitution
markdown: |-
![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)
html: |-
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/group58/project22/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" class="lazy gfm" data-src="/group58/project22/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
- name: attachment_image_for_project_wiki
api_context: project_wiki
substitutions:
uri_substitution: *uri_substitution
markdown: |-
![test-file](test-file.png)
html: |-
<p data-sourcepos="1:1-1:27" dir="auto"><a class="no-attachment-icon" href="/group1/project1/-/wikis/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="test-file.png"><img alt="test-file" class="lazy" data-src="/group1/project1/-/wikis/test-file.png" data-canonical-src="test-file.png"></a></p>
- name: attachment_link_for_group
api_context: group
substitutions:
uri_substitution: *uri_substitution
markdown: |-
[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
html: |-
<p data-sourcepos="1:1-1:68" dir="auto"><a href="/groups/group58/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
- name: attachment_link_for_project
api_context: project
substitutions:
uri_substitution: *uri_substitution
markdown: |-
[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)
html: |-
<p data-sourcepos="1:1-1:68" dir="auto"><a href="/group58/project22/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
- name: attachment_link_for_project_wiki
api_context: project_wiki
substitutions:
uri_substitution: *uri_substitution
# TODO: The current frontend example doesn't include the path, need to look into why it does after refactoring to the new golden master approach
pending:
frontend: 'The current frontend example doesnt include the path, need to look into why it does after refactoring to the new golden master approach'
markdown: |-
[test-file](test-file.zip)
html: |-
<p data-sourcepos="1:1-1:26" dir="auto"><a href="/group1/project1/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a></p>
- name: audio
markdown: |-
![Sample Audio](https://gitlab.com/gitlab.mp3)
html: |-
<p data-sourcepos="1:1-1:46" dir="auto"><span class="media-container audio-container"><audio src="https://gitlab.com/gitlab.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/gitlab.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></p>
- name: audio_and_video_in_lists
markdown: |-
* ![Sample Audio](https://gitlab.com/1.mp3)
* ![Sample Video](https://gitlab.com/2.mp4)
1. ![Sample Video](https://gitlab.com/1.mp4)
2. ![Sample Audio](https://gitlab.com/2.mp3)
* [x] ![Sample Audio](https://gitlab.com/1.mp3)
* [x] ![Sample Audio](https://gitlab.com/2.mp3)
* [x] ![Sample Video](https://gitlab.com/3.mp4)
html: |-
<ul data-sourcepos="1:1-3:0" dir="auto">
<li data-sourcepos="1:1-1:43"><span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></li>
<li data-sourcepos="2:1-3:0"><span class="media-container video-container"><video src="https://gitlab.com/2.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/2.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></li>
</ul>
<ol data-sourcepos="4:1-6:0" dir="auto">
<li data-sourcepos="4:1-4:44"><span class="media-container video-container"><video src="https://gitlab.com/1.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/1.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></li>
<li data-sourcepos="5:1-6:0"><span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span></li>
</ol>
<ul data-sourcepos="7:1-9:47" class="task-list" dir="auto">
<li data-sourcepos="7:1-7:47" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container audio-container"><audio src="https://gitlab.com/1.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/1.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span>
</li>
<li data-sourcepos="8:1-8:47" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container audio-container"><audio src="https://gitlab.com/2.mp3" controls="true" data-setup="{}" data-title="Sample Audio"></audio><a href="https://gitlab.com/2.mp3" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Audio'">Sample Audio</a></span>
</li>
<li data-sourcepos="9:1-9:47" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> <span class="media-container video-container"><video src="https://gitlab.com/3.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/3.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span>
</li>
</ul>
- name: blockquote
markdown: |-
> This is a blockquote
>
> This is another one
html: |-
<blockquote data-sourcepos="1:1-3:21" dir="auto">
<p data-sourcepos="1:3-1:22">This is a blockquote</p>
<p data-sourcepos="3:3-3:21">This is another one</p>
</blockquote>
- name: bold
markdown: |-
**bold**
html: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
- name: bullet_list_style_1
markdown: |-
* list item 1
* list item 2
* embedded list item 3
html: |-
<ul data-sourcepos="1:1-3:24" dir="auto">
<li data-sourcepos="1:1-1:13">list item 1</li>
<li data-sourcepos="2:1-3:24">list item 2
<ul data-sourcepos="3:3-3:24">
<li data-sourcepos="3:3-3:24">embedded list item 3</li>
</ul>
</li>
</ul>
- name: bullet_list_style_2
markdown: |-
- list item 1
- list item 2
* embedded list item 3
html: |-
<ul data-sourcepos="1:1-3:24" dir="auto">
<li data-sourcepos="1:1-1:13">list item 1</li>
<li data-sourcepos="2:1-3:24">list item 2
<ul data-sourcepos="3:3-3:24">
<li data-sourcepos="3:3-3:24">embedded list item 3</li>
</ul>
</li>
</ul>
- name: bullet_list_style_3
markdown: |-
+ list item 1
+ list item 2
- embedded list item 3
html: |-
<ul data-sourcepos="1:1-3:24" dir="auto">
<li data-sourcepos="1:1-1:13">list item 1</li>
<li data-sourcepos="2:1-3:24">list item 2
<ul data-sourcepos="3:3-3:24">
<li data-sourcepos="3:3-3:24">embedded list item 3</li>
</ul>
</li>
</ul>
- name: code_block
markdown: |-
```javascript
console.log('hello world')
```
html: |-
<pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-javascript" lang="javascript" v-pre="true"><code><span id="LC1" class="line" lang="javascript"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">hello world</span><span class="dl">'</span><span class="p">)</span></span></code></pre>
- name: color_chips
markdown: |-
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.3)`
- `HSL(540,70%,50%)`
- `HSLA(540,70%,50%,0.3)`
html: |-
<ul data-sourcepos="1:1-9:25" dir="auto">
<li data-sourcepos="1:1-1:8"><code>#F00<span class="gfm-color_chip"><span style="background-color: #F00;"></span></span></code></li>
<li data-sourcepos="2:1-2:9"><code>#F00A<span class="gfm-color_chip"><span style="background-color: #F00A;"></span></span></code></li>
<li data-sourcepos="3:1-3:11"><code>#FF0000<span class="gfm-color_chip"><span style="background-color: #FF0000;"></span></span></code></li>
<li data-sourcepos="4:1-4:13"><code>#FF0000AA<span class="gfm-color_chip"><span style="background-color: #FF0000AA;"></span></span></code></li>
<li data-sourcepos="5:1-5:16"><code>RGB(0,255,0)<span class="gfm-color_chip"><span style="background-color: RGB(0,255,0);"></span></span></code></li>
<li data-sourcepos="6:1-6:19"><code>RGB(0%,100%,0%)<span class="gfm-color_chip"><span style="background-color: RGB(0%,100%,0%);"></span></span></code></li>
<li data-sourcepos="7:1-7:21"><code>RGBA(0,255,0,0.3)<span class="gfm-color_chip"><span style="background-color: RGBA(0,255,0,0.3);"></span></span></code></li>
<li data-sourcepos="8:1-8:20"><code>HSL(540,70%,50%)<span class="gfm-color_chip"><span style="background-color: HSL(540,70%,50%);"></span></span></code></li>
<li data-sourcepos="9:1-9:25"><code>HSLA(540,70%,50%,0.3)<span class="gfm-color_chip"><span style="background-color: HSLA(540,70%,50%,0.3);"></span></span></code></li>
</ul>
- name: description_list
markdown: |-
<dl>
<dt>Frog</dt>
<dd>Wet green thing</dd>
<dt>Rabbit</dt>
<dd>Warm fluffy thing</dd>
<dt>Punt</dt>
<dd>Kick a ball</dd>
<dd>Take a bet</dd>
<dt>Color</dt>
<dt>Colour</dt>
<dd>
Any hue except _white_ or **black**
</dd>
</dl>
html: |-
<dl>
<dt>Frog</dt>
<dd>Wet green thing</dd>
<dt>Rabbit</dt>
<dd>Warm fluffy thing</dd>
<dt>Punt</dt>
<dd>Kick a ball</dd>
<dd>Take a bet</dd>
<dt>Color</dt>
<dt>Colour</dt>
<dd>
<p data-sourcepos="13:1-13:35">Any hue except <em>white</em> or <strong>black</strong></p>
</dd>
</dl>
- name: details
markdown: |-
<details>
<summary>This is the visible summary of the collapsible section</summary>
1. collapsed markdown
2. more collapsed markdown
</details>
html: |-
<details>
<summary>This is the visible summary of the collapsible section</summary>
<ol data-sourcepos="4:1-6:0">
<li data-sourcepos="4:1-4:21">collapsed markdown</li>
<li data-sourcepos="5:1-6:0">more collapsed markdown</li>
</ol>
</details>
- name: div
markdown: |-
<div>plain text</div>
<div>
just a plain ol' div, not much to _expect_!
</div>
html: |-
<div>plain text</div>
<div>
<p data-sourcepos="4:1-4:43">just a plain ol' div, not much to <em>expect</em>!</p>
</div>
- name: emoji
markdown: |-
:sparkles: :heart: :100:
html: |-
<p data-sourcepos="1:1-1:24" dir="auto"><gl-emoji title="sparkles" data-name="sparkles" data-unicode-version="6.0">✨</gl-emoji> <gl-emoji title="heavy black heart" data-name="heart" data-unicode-version="1.1">❤</gl-emoji> <gl-emoji title="hundred points symbol" data-name="100" data-unicode-version="6.0">💯</gl-emoji></p>
- name: emphasis
markdown: _emphasized text_
html: <p data-sourcepos="1:1-1:17" dir="auto"><em>emphasized text</em></p>
- name: figure
markdown: |-
<figure>
![Elephant at sunset](elephant-sunset.jpg)
<figcaption>An elephant at sunset</figcaption>
</figure>
<figure>
![A crocodile wearing crocs](croc-crocs.jpg)
<figcaption>
A crocodile wearing _crocs_!
</figcaption>
</figure>
html: |-
<figure>
<p data-sourcepos="3:1-3:42"><a class="no-attachment-icon" href="elephant-sunset.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Elephant at sunset" class="lazy" data-src="elephant-sunset.jpg"></a></p>
<figcaption>An elephant at sunset</figcaption>
</figure>
<figure>
<p data-sourcepos="9:1-9:44"><a class="no-attachment-icon" href="croc-crocs.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="A crocodile wearing crocs" class="lazy" data-src="croc-crocs.jpg"></a></p>
<figcaption>
<p data-sourcepos="13:1-13:28">A crocodile wearing <em>crocs</em>!</p>
</figcaption>
</figure>
- name: frontmatter_json
markdown: |-
;;;
{
"title": "Page title"
}
;;;
html: |-
<pre data-sourcepos="1:1-5:3" class="code highlight js-syntax-highlight language-json" lang="json" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="json"><span class="p">{</span></span>
<span id="LC2" class="line" lang="json"><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Page title"</span></span>
<span id="LC3" class="line" lang="json"><span class="p">}</span></span></code></pre>
- name: frontmatter_toml
markdown: |-
+++
title = "Page title"
+++
html: <pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-toml" lang="toml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="toml"><span class="py">title</span> <span class="p">=</span> <span class="s">"Page title"</span></span></code></pre>
- name: frontmatter_yaml
markdown: |-
---
title: Page title
---
html: <pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-yaml" lang="yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">Page title</span></span></code></pre>
- name: hard_break
markdown: |-
This is a line after a\
hard break
html: |-
<p data-sourcepos="1:1-2:10" dir="auto">This is a line after a<br>
hard break</p>
- name: headings
markdown: |-
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
html: |-
<h1 data-sourcepos="1:1-1:11" dir="auto">
<a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1>
<h2 data-sourcepos="3:1-3:12" dir="auto">
<a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2>
<h3 data-sourcepos="5:1-5:13" dir="auto">
<a id="user-content-heading-3" class="anchor" href="#heading-3" aria-hidden="true"></a>Heading 3</h3>
<h4 data-sourcepos="7:1-7:14" dir="auto">
<a id="user-content-heading-4" class="anchor" href="#heading-4" aria-hidden="true"></a>Heading 4</h4>
<h5 data-sourcepos="9:1-9:15" dir="auto">
<a id="user-content-heading-5" class="anchor" href="#heading-5" aria-hidden="true"></a>Heading 5</h5>
<h6 data-sourcepos="11:1-11:16" dir="auto">
<a id="user-content-heading-6" class="anchor" href="#heading-6" aria-hidden="true"></a>Heading 6</h6>
- name: horizontal_rule
markdown: |-
---
html: |-
<hr data-sourcepos="1:1-1:3">
- name: html_marks
markdown: |-
* Content editor is ~~great~~<ins>amazing</ins>.
* If the changes <abbr title="Looks good to merge">LGTM</abbr>, please <abbr title="Merge when pipeline succeeds">MWPS</abbr>.
* The English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew: <span dir="rtl">אה, אני אוהב להיות ליד חוף הים</span>. In the computer's memory, this is stored as <bdo dir="ltr">אה, אני אוהב להיות ליד חוף הים</bdo>.
* <cite>The Scream</cite> by Edvard Munch. Painted in 1893.
* <dfn>HTML</dfn> is the standard markup language for creating web pages.
* Do not forget to buy <mark>milk</mark> today.
* This is a paragraph and <small>smaller text goes here</small>.
* The concert starts at <time datetime="20:00">20:00</time> and you'll be able to enjoy the band for at least <time datetime="PT2H30M">2h 30m</time>.
* Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text (Windows).
* WWF's goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.
* The error occured was: <samp>Keyboard not found. Press F1 to continue.</samp>
* The area of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var> is the vertical height.
* <ruby>漢<rt>ㄏㄢˋ</rt></ruby>
* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
html: |-
<ul data-sourcepos="1:1-15:130" dir="auto">
<li data-sourcepos="1:1-1:48">Content editor is <del>great</del><ins>amazing</ins>.</li>
<li data-sourcepos="2:1-2:126">If the changes <abbr title="Looks good to merge">LGTM</abbr>, please <abbr title="Merge when pipeline succeeds">MWPS</abbr>.</li>
<li data-sourcepos="3:1-3:288">The English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew: <span dir="rtl">אה, אני אוהב להיות ליד חוף הים</span>. In the computer's memory, this is stored as <bdo dir="ltr">אה, אני אוהב להיות ליד חוף הים</bdo>.</li>
<li data-sourcepos="4:1-4:59">
<cite>The Scream</cite> by Edvard Munch. Painted in 1893.</li>
<li data-sourcepos="5:1-5:73">
<dfn>HTML</dfn> is the standard markup language for creating web pages.</li>
<li data-sourcepos="6:1-6:47">Do not forget to buy <mark>milk</mark> today.</li>
<li data-sourcepos="7:1-7:64">This is a paragraph and <small>smaller text goes here</small>.</li>
<li data-sourcepos="8:1-8:149">The concert starts at <time datetime="20:00">20:00</time> and you'll be able to enjoy the band for at least <time datetime="PT2H30M">2h 30m</time>.</li>
<li data-sourcepos="9:1-9:62">Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text (Windows).</li>
<li data-sourcepos="10:1-10:105">WWF's goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.</li>
<li data-sourcepos="11:1-11:79">The error occured was: <samp>Keyboard not found. Press F1 to continue.</samp>
</li>
<li data-sourcepos="12:1-12:136">The area of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var> is the vertical height.</li>
<li data-sourcepos="13:1-13:35"><ruby>漢<rt>ㄏㄢˋ</rt></ruby></li>
<li data-sourcepos="14:1-14:81">C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O</li>
<li data-sourcepos="15:1-15:130">The <strong>Pythagorean theorem</strong> is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
</li>
</ul>
- name: image
markdown: |-
![alt text](https://gitlab.com/logo.png)
html: |-
<p data-sourcepos="1:1-1:40" dir="auto"><a class="no-attachment-icon" href="https://gitlab.com/logo.png" target="_blank" rel="nofollow noreferrer noopener"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="alt text" class="lazy" data-src="https://gitlab.com/logo.png"></a></p>
- name: inline_code
markdown: |-
`code`
html: |-
<p data-sourcepos="1:1-1:6" dir="auto"><code>code</code></p>
- name: inline_diff
markdown: |-
* {-deleted-}
* {+added+}
html: |-
<ul data-sourcepos="1:1-2:11" dir="auto">
<li data-sourcepos="1:1-1:13"><span class="idiff left right deletion">deleted</span></li>
<li data-sourcepos="2:1-2:11"><span class="idiff left right addition">added</span></li>
</ul>
- name: label
pending:
# TODO: There is an error with the frontend HTML to markdown spec adding a double escape (\\) to the label tilde.
frontend: 'There is an error with the frontend HTML to markdown spec adding a double escape (\\) to the label tilde.'
markdown: |-
~bug
html: |-
<p data-sourcepos="1:1-1:4" dir="auto">~bug</p>
- name: link
markdown: |-
[GitLab](https://gitlab.com)
html: |-
<p data-sourcepos="1:1-1:28" dir="auto"><a href="https://gitlab.com" rel="nofollow noreferrer noopener" target="_blank">GitLab</a></p>
- name: math
markdown: |-
This math is inline $`a^2+b^2=c^2`$.
This is on a separate line:
```math
a^2+b^2=c^2
```
html: |-
<p data-sourcepos="1:1-1:36" dir="auto">This math is inline <code class="code math js-render-math" data-math-style="inline">a^2+b^2=c^2</code>.</p>
<p data-sourcepos="3:1-3:27" dir="auto">This is on a separate line:</p>
<pre data-sourcepos="5:1-7:3" class="code highlight js-syntax-highlight language-math js-render-math" lang="math" v-pre="true" data-math-style="display"><code><span id="LC1" class="line" lang="math">a^2+b^2=c^2</span></code></pre>
- name: ordered_list
markdown: |-
1. list item 1
2. list item 2
3. list item 3
html: |-
<ol data-sourcepos="1:1-3:14" dir="auto">
<li data-sourcepos="1:1-1:14">list item 1</li>
<li data-sourcepos="2:1-2:14">list item 2</li>
<li data-sourcepos="3:1-3:14">list item 3</li>
</ol>
- name: ordered_list_with_start_order
markdown: |-
134. list item 1
135. list item 2
136. list item 3
html: |-
<ol start="134" data-sourcepos="1:1-3:16" dir="auto">
<li data-sourcepos="1:1-1:16">list item 1</li>
<li data-sourcepos="2:1-2:16">list item 2</li>
<li data-sourcepos="3:1-3:16">list item 3</li>
</ol>
- name: ordered_task_list
markdown: |-
1. [x] hello
2. [x] world
3. [ ] example
1. [ ] of nested
1. [x] task list
2. [ ] items
html: |-
<ol data-sourcepos="1:1-6:18" class="task-list" dir="auto">
<li data-sourcepos="1:1-1:12" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
<li data-sourcepos="2:1-2:12" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
<li data-sourcepos="3:1-6:18" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> example
<ol data-sourcepos="4:4-6:18" class="task-list">
<li data-sourcepos="4:4-6:18" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> of nested
<ol data-sourcepos="5:7-6:18" class="task-list">
<li data-sourcepos="5:7-5:22" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> task list</li>
<li data-sourcepos="6:7-6:18" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> items</li>
</ol>
</li>
</ol>
</li>
</ol>
- name: ordered_task_list_with_order
markdown: |-
4893. [x] hello
4894. [x] world
4895. [ ] example
html: |-
<ol start="4893" data-sourcepos="1:1-3:17" class="task-list" dir="auto">
<li data-sourcepos="1:1-1:15" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
<li data-sourcepos="2:1-2:15" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
<li data-sourcepos="3:1-3:17" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> example</li>
</ol>
- name: reference_for_project_wiki
api_context: project_wiki
substitutions:
# NOTE: We don't care about verifying specific attribute values here, that should be the
# responsibility of unit tests. These tests are about the structure of the HTML.
uri_substitution: *uri_substitution
data_attribute_id_substitution:
- regex: '(data-user|data-project|data-issue|data-iid|data-merge-request|data-milestone)(=")(\d+?)(")'
replacement: '\1\2ID\4'
text_attribute_substitution:
- regex: '(title)(=")(.+?)(")'
replacement: '\1\2TEXT\4'
path_attribute_id_substitution:
- regex: '(group|project)(\d+)'
replacement: '\1ID'
markdown: |-
Hi @gfm_user - thank you for reporting this bug (#1) we hope to fix it in %1.1 as part of !1
html: |-
<p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-user="1" data-reference-type="user" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-reference-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue has-tooltip">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-reference-type="milestone" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-mr-title="My title 2" data-reference-type="merge_request" data-container="body" data-placement="top" title="" class="gfm gfm-merge_request">!1</a></p>
- name: strike
markdown: |-
~~del~~
html: |-
<p data-sourcepos="1:1-1:7" dir="auto"><del>del</del></p>
- name: table
markdown: |-
| header | header |
|--------|--------|
| `code` | cell with **bold** |
| ~~strike~~ | cell with _italic_ |
# content after table
html: |-
<table data-sourcepos="1:1-4:35" dir="auto">
<thead>
<tr data-sourcepos="1:1-1:19">
<th data-sourcepos="1:2-1:9">header</th>
<th data-sourcepos="1:11-1:18">header</th>
</tr>
</thead>
<tbody>
<tr data-sourcepos="3:1-3:31">
<td data-sourcepos="3:2-3:9"><code>code</code></td>
<td data-sourcepos="3:11-3:30">cell with <strong>bold</strong>
</td>
</tr>
<tr data-sourcepos="4:1-4:35">
<td data-sourcepos="4:2-4:13"><del>strike</del></td>
<td data-sourcepos="4:15-4:34">cell with <em>italic</em>
</td>
</tr>
</tbody>
</table>
<h1 data-sourcepos="6:1-6:21" dir="auto">
<a id="user-content-content-after-table" class="anchor" href="#content-after-table" aria-hidden="true"></a>content after table</h1>
- name: table_of_contents
markdown: |-
[[_TOC_]]
# Lorem
Well, that's just like... your opinion.. man.
## Ipsum
### Dolar
# Sit amit
### I don't know
html: |-
<ul class="section-nav">
<li>
<a href="#lorem">Lorem</a><ul><li>
<a href="#ipsum">Ipsum</a><ul><li><a href="#dolar">Dolar</a></li></ul>
</li></ul>
</li>
<li>
<a href="#sit-amit">Sit amit</a><ul><li><a href="#i-dont-know">I don't know</a></li></ul>
</li>
</ul>
<h1 data-sourcepos="3:1-3:7" dir="auto">
<a id="user-content-lorem" class="anchor" href="#lorem" aria-hidden="true"></a>Lorem</h1>
<p data-sourcepos="5:1-5:45" dir="auto">Well, that's just like... your opinion.. man.</p>
<h2 data-sourcepos="7:1-7:8" dir="auto">
<a id="user-content-ipsum" class="anchor" href="#ipsum" aria-hidden="true"></a>Ipsum</h2>
<h3 data-sourcepos="9:1-9:9" dir="auto">
<a id="user-content-dolar" class="anchor" href="#dolar" aria-hidden="true"></a>Dolar</h3>
<h1 data-sourcepos="11:1-11:10" dir="auto">
<a id="user-content-sit-amit" class="anchor" href="#sit-amit" aria-hidden="true"></a>Sit amit</h1>
<h3 data-sourcepos="13:1-13:16" dir="auto">
<a id="user-content-i-dont-know" class="anchor" href="#i-dont-know" aria-hidden="true"></a>I don't know</h3>
- name: task_list
markdown: |-
* [x] hello
* [x] world
* [ ] example
* [ ] of nested
* [x] task list
* [ ] items
html: |-
<ul data-sourcepos="1:1-6:15" class="task-list" dir="auto">
<li data-sourcepos="1:1-1:11" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> hello</li>
<li data-sourcepos="2:1-2:11" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> world</li>
<li data-sourcepos="3:1-6:15" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> example
<ul data-sourcepos="4:3-6:15" class="task-list">
<li data-sourcepos="4:3-6:15" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> of nested
<ul data-sourcepos="5:5-6:15" class="task-list">
<li data-sourcepos="5:5-5:19" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" checked disabled> task list</li>
<li data-sourcepos="6:5-6:15" class="task-list-item">
<input type="checkbox" class="task-list-item-checkbox" disabled> items</li>
</ul>
</li>
</ul>
</li>
</ul>
- name: video
markdown: |-
![Sample Video](https://gitlab.com/gitlab.mp4)
html: |-
<p data-sourcepos="1:1-1:46" dir="auto"><span class="media-container video-container"><video src="https://gitlab.com/gitlab.mp4" controls="true" data-setup="{}" data-title="Sample Video" width="400" preload="metadata"></video><a href="https://gitlab.com/gitlab.mp4" target="_blank" rel="nofollow noreferrer noopener" title="Download 'Sample Video'">Sample Video</a></span></p>
- name: word_break
markdown: Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz
html: <p data-sourcepos="1:1-1:60" dir="auto">Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz</wbr></wbr></wbr></wbr></p>

View file

@ -1,27 +0,0 @@
import fs from 'fs';
import path from 'path';
import jsYaml from 'js-yaml';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
export const loadMarkdownApiResult = (testName) => {
const fixturePathPrefix = `api/markdown/${testName}.json`;
// eslint-disable-next-line import/no-deprecated
const fixture = getJSONFixture(fixturePathPrefix);
return fixture.body || fixture.html;
};
export const loadMarkdownApiExamples = () => {
const apiMarkdownYamlPath = path.join(__dirname, '..', 'fixtures', 'api_markdown.yml');
const apiMarkdownYamlText = fs.readFileSync(apiMarkdownYamlPath);
const apiMarkdownExampleObjects = jsYaml.safeLoad(apiMarkdownYamlText);
return apiMarkdownExampleObjects.map(({ name, context, markdown }) => [name, context, markdown]);
};
export const loadMarkdownApiExample = (testName) => {
return loadMarkdownApiExamples().find(([name, context]) => {
return (context ? `${context}_${name}` : name) === testName;
})[2];
};

View file

@ -1,20 +1,19 @@
import { createContentEditor } from '~/content_editor';
import { loadMarkdownApiExamples, loadMarkdownApiResult } from './markdown_processing_examples';
import path from 'path';
import { createSharedExamples, loadMarkdownApiExamples } from './markdown_processing_spec_helper';
jest.mock('~/emoji');
describe('markdown processing', () => {
// See spec/fixtures/markdown/markdown_golden_master_examples.yml for documentation on how this spec works.
describe('markdown processing in ContentEditor', () => {
// Ensure we generate same markdown that was provided to Markdown API.
it.each(loadMarkdownApiExamples())(
'correctly handles %s (context: %s)',
async (name, context, markdown) => {
const testName = context ? `${context}_${name}` : name;
const contentEditor = createContentEditor({
renderMarkdown: () => loadMarkdownApiResult(testName),
});
await contentEditor.setSerializedContent(markdown);
expect(contentEditor.getSerializedContent()).toBe(markdown);
},
const markdownYamlPath = path.join(
__dirname,
'..',
'..',
'fixtures',
'markdown',
'markdown_golden_master_examples.yml',
);
// eslint-disable-next-line jest/valid-describe
describe.each(loadMarkdownApiExamples(markdownYamlPath))('%s', createSharedExamples);
});

View file

@ -0,0 +1,75 @@
import fs from 'fs';
import jsYaml from 'js-yaml';
import { memoize } from 'lodash';
import { createContentEditor } from '~/content_editor';
import { setTestTimeoutOnce } from 'helpers/timeout';
const getFocusedMarkdownExamples = memoize(
() => process.env.FOCUSED_MARKDOWN_EXAMPLES?.split(',') || [],
);
const includeExample = ({ name }) => {
const focusedMarkdownExamples = getFocusedMarkdownExamples();
if (!focusedMarkdownExamples.length) {
return true;
}
return focusedMarkdownExamples.includes(name);
};
const getPendingReason = (pendingStringOrObject) => {
if (!pendingStringOrObject) {
return null;
}
if (typeof pendingStringOrObject === 'string') {
return pendingStringOrObject;
}
if (pendingStringOrObject.frontend) {
return pendingStringOrObject.frontend;
}
return null;
};
// eslint-disable-next-line jest/no-export
export const loadMarkdownApiExamples = (markdownYamlPath) => {
const apiMarkdownYamlText = fs.readFileSync(markdownYamlPath);
const apiMarkdownExampleObjects = jsYaml.safeLoad(apiMarkdownYamlText);
return apiMarkdownExampleObjects
.filter(includeExample)
.map(({ name, pending, markdown, html }) => [
name,
{ pendingReason: getPendingReason(pending), markdown, html },
]);
};
const testSerializesHtmlToMarkdownForElement = async ({ markdown, html }) => {
const contentEditor = createContentEditor({
// Overwrite renderMarkdown to always return this specific html
renderMarkdown: () => html,
});
await contentEditor.setSerializedContent(markdown);
// This serializes the ContentEditor document, which was based on the HTML, to markdown
const serializedContent = contentEditor.getSerializedContent();
// Assert that the markdown we ended up with after sending it through all the ContentEditor
// plumbing matches the original markdown from the YAML.
expect(serializedContent).toBe(markdown);
};
// eslint-disable-next-line jest/no-export
export const createSharedExamples = (name, { pendingReason, ...example }) => {
const exampleName = 'correctly serializes HTML to markdown';
if (pendingReason) {
it.todo(`${exampleName}: ${pendingReason}`);
} else {
it(exampleName, async () => {
if (name === 'frontmatter_toml') {
setTestTimeoutOnce(2000);
}
await testSerializesHtmlToMarkdownForElement(example);
});
}
};

View file

@ -1,65 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::MergeRequests, '(JavaScript fixtures)', type: :request do
include ApiHelpers
include WikiHelpers
include JavaScriptFixturesHelpers
let_it_be(:user) { create(:user, username: 'gitlab') }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:label) { create(:label, project: project, title: 'bug') }
let_it_be(:milestone) { create(:milestone, project: project, title: '1.1') }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
let_it_be(:project_wiki) { create(:project_wiki, project: project, user: user) }
let(:project_wiki_page) { create(:wiki_page, wiki: project_wiki) }
before(:all) do
group.add_owner(user)
project.add_maintainer(user)
end
before do
sign_in(user)
end
markdown_examples = begin
yaml_file_path = File.expand_path('api_markdown.yml', __dir__)
yaml = File.read(yaml_file_path)
YAML.safe_load(yaml, symbolize_names: true)
end
markdown_examples.each do |markdown_example|
context = markdown_example.fetch(:context, '')
name = markdown_example.fetch(:name)
context "for #{name}#{!context.empty? ? " (context: #{context})" : ''}" do
let(:markdown) { markdown_example.fetch(:markdown) }
name = "#{context}_#{name}" unless context.empty?
it "api/markdown/#{name}.json" do
api_url = case context
when 'project'
"/#{project.full_path}/preview_markdown"
when 'group'
"/groups/#{group.full_path}/preview_markdown"
when 'project_wiki'
"/#{project.full_path}/-/wikis/#{project_wiki_page.slug}/preview_markdown"
else
api "/markdown"
end
post api_url, params: { text: markdown, gfm: true }
expect(response).to be_successful
end
end
end
end

View file

@ -1,289 +0,0 @@
# This data file drives the specs in
# spec/frontend/fixtures/api_markdown.rb and
# spec/frontend/content_editor/extensions/markdown_processing_spec.js
---
- name: attachment_image
context: group
markdown: '![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)'
- name: attachment_image
context: project
markdown: '![test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png)'
- name: attachment_image
context: project_wiki
markdown: '![test-file](test-file.png)'
- name: attachment_link
context: group
markdown: '[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)'
- name: attachment_link
context: project
markdown: '[test-file](/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip)'
- name: attachment_link
context: project_wiki
markdown: '[test-file](test-file.zip)'
- name: audio
markdown: '![Sample Audio](https://gitlab.com/gitlab.mp3)'
- name: audio_and_video_in_lists
markdown: |-
* ![Sample Audio](https://gitlab.com/1.mp3)
* ![Sample Video](https://gitlab.com/2.mp4)
1. ![Sample Video](https://gitlab.com/1.mp4)
2. ![Sample Audio](https://gitlab.com/2.mp3)
* [x] ![Sample Audio](https://gitlab.com/1.mp3)
* [x] ![Sample Audio](https://gitlab.com/2.mp3)
* [x] ![Sample Video](https://gitlab.com/3.mp4)
- name: blockquote
markdown: |-
> This is a blockquote
>
> This is another one
- name: bold
markdown: '**bold**'
- name: bullet_list_style_1
markdown: |-
* list item 1
* list item 2
* embedded list item 3
- name: bullet_list_style_2
markdown: |-
- list item 1
- list item 2
* embedded list item 3
- name: bullet_list_style_3
markdown: |-
+ list item 1
+ list item 2
- embedded list item 3
- name: code_block
markdown: |-
```javascript
console.log('hello world')
```
- name: color_chips
markdown: |-
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.3)`
- `HSL(540,70%,50%)`
- `HSLA(540,70%,50%,0.3)`
- name: description_list
markdown: |-
<dl>
<dt>Frog</dt>
<dd>Wet green thing</dd>
<dt>Rabbit</dt>
<dd>Warm fluffy thing</dd>
<dt>Punt</dt>
<dd>Kick a ball</dd>
<dd>Take a bet</dd>
<dt>Color</dt>
<dt>Colour</dt>
<dd>
Any hue except _white_ or **black**
</dd>
</dl>
- name: details
markdown: |-
<details>
<summary>Apply this patch</summary>
```diff
diff --git a/spec/frontend/fixtures/api_markdown.yml b/spec/frontend/fixtures/api_markdown.yml
index 8433efaf00c..69b12c59d46 100644
--- a/spec/frontend/fixtures/api_markdown.yml
+++ b/spec/frontend/fixtures/api_markdown.yml
@@ -33,6 +33,13 @@
* <ruby>漢<rt>ㄏㄢˋ</rt></ruby>
* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>.The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
+- name: details
+ markdown: |-
+ <details>
+ <summary>Apply this patch</summary>
+
+ 🐶 much meta, 🐶 many patch
+ 🐶 such diff, 🐶 very meme
+ 🐶 wow!
+ </details>
- name: link
markdown: '[GitLab](https://gitlab.com)'
- name: attachment_link
```
</details>
- name: div
markdown: |-
<div>plain text</div>
<div>
just a plain ol' div, not much to _expect_!
</div>
- name: emoji
markdown: ':sparkles: :heart: :100:'
- name: emphasis
markdown: '_emphasized text_'
- name: figure
markdown: |-
<figure>
![Elephant at sunset](elephant-sunset.jpg)
<figcaption>An elephant at sunset</figcaption>
</figure>
<figure>
![A crocodile wearing crocs](croc-crocs.jpg)
<figcaption>
A crocodile wearing _crocs_!
</figcaption>
</figure>
- name: frontmatter_json
markdown: |-
;;;
{
"title": "Page title"
}
;;;
- name: frontmatter_toml
markdown: |-
+++
title = "Page title"
+++
- name: frontmatter_yaml
markdown: |-
---
title: Page title
---
- name: hard_break
markdown: |-
This is a line after a\
hard break
- name: headings
markdown: |-
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
- name: horizontal_rule
markdown: '---'
- name: html_marks
markdown: |-
* Content editor is ~~great~~<ins>amazing</ins>.
* If the changes <abbr title="Looks good to merge">LGTM</abbr>, please <abbr title="Merge when pipeline succeeds">MWPS</abbr>.
* The English song <q>Oh I do like to be beside the seaside</q> looks like this in Hebrew: <span dir="rtl">אה, אני אוהב להיות ליד חוף הים</span>. In the computer's memory, this is stored as <bdo dir="ltr">אה, אני אוהב להיות ליד חוף הים</bdo>.
* <cite>The Scream</cite> by Edvard Munch. Painted in 1893.
* <dfn>HTML</dfn> is the standard markup language for creating web pages.
* Do not forget to buy <mark>milk</mark> today.
* This is a paragraph and <small>smaller text goes here</small>.
* The concert starts at <time datetime="20:00">20:00</time> and you'll be able to enjoy the band for at least <time datetime="PT2H30M">2h 30m</time>.
* Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy text (Windows).
* WWF's goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.
* The error occured was: <samp>Keyboard not found. Press F1 to continue.</samp>
* The area of a triangle is: 1/2 x <var>b</var> x <var>h</var>, where <var>b</var> is the base, and <var>h</var> is the vertical height.
* <ruby>漢<rt>ㄏㄢˋ</rt></ruby>
* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
- name: image
markdown: '![alt text](https://gitlab.com/logo.png)'
- name: inline_code
markdown: '`code`'
- name: inline_diff
markdown: |-
* {-deleted-}
* {+added+}
- name: link
markdown: '[GitLab](https://gitlab.com)'
- name: math
markdown: |-
This math is inline $`a^2+b^2=c^2`$.
This is on a separate line:
```math
a^2+b^2=c^2
```
- name: ordered_list
markdown: |-
1. list item 1
2. list item 2
3. list item 3
- name: ordered_list_with_start_order
markdown: |-
134. list item 1
135. list item 2
136. list item 3
- name: ordered_task_list
markdown: |-
1. [x] hello
2. [x] world
3. [ ] example
1. [ ] of nested
1. [x] task list
2. [ ] items
- name: ordered_task_list_with_order
markdown: |-
4893. [x] hello
4894. [x] world
4895. [ ] example
- name: reference
context: project_wiki
markdown: |-
Hi @gitlab - thank you for reporting this ~bug (#1) we hope to fix it in %1.1 as part of !1
- name: strike
markdown: '~~del~~'
- name: table
markdown: |-
| header | header |
|--------|--------|
| `code` | cell with **bold** |
| ~~strike~~ | cell with _italic_ |
# content after table
- name: table_of_contents
markdown: |-
[[_TOC_]]
# Lorem
Well, that's just like... your opinion.. man.
## Ipsum
### Dolar
# Sit amit
### I don't know
- name: task_list
markdown: |-
* [x] hello
* [x] world
* [ ] example
* [ ] of nested
* [x] task list
* [ ] items
- name: thematic_break
markdown: |-
---
- name: video
markdown: '![Sample Video](https://gitlab.com/gitlab.mp4)'
- name: word_break
markdown: Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
# See spec/fixtures/markdown/markdown_golden_master_examples.yml for documentation on how this spec works.
RSpec.describe API::Markdown, 'Golden Master' do
markdown_yml_file_path = File.expand_path('../../fixtures/markdown/markdown_golden_master_examples.yml', __dir__)
include_context 'API::Markdown Golden Master shared context', markdown_yml_file_path
end

View file

@ -112,7 +112,7 @@ RSpec.describe RuboCop::Cop::StaticTranslationDefinition do
}
end
CODE
<<~CODE
<<~CODE,
class MyClass
def hello
{
@ -121,6 +121,20 @@ RSpec.describe RuboCop::Cop::StaticTranslationDefinition do
end
end
CODE
<<~CODE,
SomeClass = Struct.new do
def text
_('Some translated text')
end
end
CODE
<<~CODE
Struct.new('SomeClass') do
def text
_('Some translated text')
end
end
CODE
]
end

View file

@ -450,6 +450,13 @@ RSpec.configure do |config|
$stdout = StringIO.new
end
# Makes diffs show entire non-truncated values.
config.before(:each, unlimited_max_formatted_output_length: true) do |_example|
config.expect_with :rspec do |c|
c.max_formatted_output_length = nil
end
end
config.after(:each, :silence_stdout) do
$stdout = STDOUT
end

View file

@ -0,0 +1,127 @@
# frozen_string_literal: true
require 'spec_helper'
# See spec/fixtures/markdown/markdown_golden_master_examples.yml for documentation on how this spec works.
RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_yml_file_path|
include ApiHelpers
include WikiHelpers
let_it_be(:user) { create(:user, username: 'gfm_user') }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:label) { create(:label, project: project, title: 'bug') }
let_it_be(:milestone) { create(:milestone, project: project, title: '1.1') }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
let_it_be(:project_wiki) { create(:project_wiki, project: project, user: user) }
let_it_be(:project_wiki_page) { create(:wiki_page, wiki: project_wiki) }
before(:all) do
group.add_owner(user)
project.add_maintainer(user)
end
before do
sign_in(user)
end
markdown_examples = begin
yaml = File.read(markdown_yml_file_path)
YAML.safe_load(yaml, symbolize_names: true, aliases: true)
end
it "examples must be unique and alphabetized by name", :unlimited_max_formatted_output_length do
names = markdown_examples.map { |example| example[:name] }
expect(names).to eq(names.sort.uniq)
end
if focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES']
focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip) || []
markdown_examples.reject! {|markdown_example| !focused_markdown_examples.include?(markdown_example.fetch(:name)) }
end
markdown_examples.each do |markdown_example|
name = markdown_example.fetch(:name)
api_context = markdown_example[:api_context]
if api_context && !name.end_with?("_for_#{api_context}")
raise "Name must have suffix of '_for_#{api_context}' to the api_context"
end
context "for #{name}#{api_context ? " (api_context: #{api_context})" : ''}" do
let(:pending_reason) do
pending_value = markdown_example.fetch(:pending, nil)
get_pending_reason(pending_value)
end
let(:example_markdown) { markdown_example.fetch(:markdown) }
let(:example_html) { markdown_example.fetch(:html) }
let(:substitutions) { markdown_example.fetch(:substitutions, {}) }
it "verifies conversion of GFM to HTML", :unlimited_max_formatted_output_length do
pending pending_reason if pending_reason
normalized_example_html = normalize_html(example_html, substitutions)
api_url = get_url_for_api_context(api_context)
post api_url, params: { text: example_markdown, gfm: true }
expect(response).to be_successful
response_body = Gitlab::Json.parse(response.body)
# Some requests have the HTML in the `html` key, others in the `body` key.
response_html = response_body['body'] ? response_body.fetch('body') : response_body.fetch('html')
normalized_response_html = normalize_html(response_html, substitutions)
expect(normalized_response_html).to eq(normalized_example_html)
end
def get_pending_reason(pending_value)
return false unless pending_value
return pending_value if pending_value.is_a?(String)
pending_value[:backend] || false
end
def normalize_html(html, substitutions)
normalized_html = html.dup
# Note: having the top level `substitutions` data structure be a hash of arrays
# allows us to compose multiple substitutions via YAML anchors (YAML anchors
# pointing to arrays can't be combined)
substitutions.each_value do |substitution_entry|
substitution_entry.each do |substitution|
regex = substitution.fetch(:regex)
replacement = substitution.fetch(:replacement)
normalized_html.gsub!(%r{#{regex}}, replacement)
end
end
normalized_html
end
end
end
def supported_api_contexts
%w(project group project_wiki)
end
def get_url_for_api_context(api_context)
case api_context
when 'project'
"/#{project.full_path}/preview_markdown"
when 'group'
"/groups/#{group.full_path}/preview_markdown"
when 'project_wiki'
"/#{project.full_path}/-/wikis/#{project_wiki_page.slug}/preview_markdown"
when nil
api "/markdown"
else
raise "Error: 'context' extension was '#{api_context}'. It must be one of: #{supported_api_contexts.join(',')}"
end
end
end