debian-mirror-gitlab/spec/features/markdown/copy_as_gfm_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

902 lines
28 KiB
Ruby
Raw Normal View History

2019-10-12 21:52:04 +05:30
# frozen_string_literal: true
2017-08-17 22:00:37 +05:30
require 'spec_helper'
2023-03-04 22:38:38 +05:30
RSpec.describe 'Copy as GFM', :js, feature_category: :team_planning do
2017-08-17 22:00:37 +05:30
include MarkupHelper
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
describe 'Copying rendered GFM' do
before do
@feat = MarkdownFeature.new
# `markdown` helper expects a `@project` variable
@project = @feat.project
2021-02-22 17:27:13 +05:30
user = create(:user)
@project.add_maintainer(user)
sign_in(user)
2017-09-10 17:25:29 +05:30
visit project_issue_path(@project, @feat.issue)
2017-08-17 22:00:37 +05:30
end
2019-03-02 22:35:43 +05:30
# The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb transform GitLab Flavored Markdown (GFM) to HTML.
# The nodes and marks referenced in app/assets/javascripts/behaviors/markdown/editor_extensions.js consequently transform that same HTML to GFM.
# To make sure these filters and nodes/marks are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle
2017-08-17 22:00:37 +05:30
# by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper.
# These are all in a single `it` for performance reasons.
2023-03-04 22:38:38 +05:30
it 'transforms HTML to GFM', :aggregate_failures do
2017-08-17 22:00:37 +05:30
verify(
'nesting',
'> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**'
)
verify(
'a real world example from the gitlab-ce README',
2019-03-02 22:35:43 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
# GitLab
2019-12-04 20:38:33 +05:30
[![Build status](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-foss/commits/master)
2019-03-02 22:35:43 +05:30
2019-12-04 20:38:33 +05:30
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-foss/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
2019-03-02 22:35:43 +05:30
2017-08-17 22:00:37 +05:30
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
2019-03-02 22:35:43 +05:30
2017-08-17 22:00:37 +05:30
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
## Canonical source
2019-12-04 20:38:33 +05:30
The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-foss/).
2017-08-17 22:00:37 +05:30
## Open source software to collaborate on code
To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
2019-03-02 22:35:43 +05:30
* Manage Git repositories with fine grained access controls that keep your code secure
* Perform code reviews and enhance collaboration with merge requests
* Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications
* Each project can also have an issue tracker, issue board, and a wiki
* Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
* Completely free and open source (MIT Expat license)
2017-08-17 22:00:37 +05:30
GFM
)
2017-09-10 17:25:29 +05:30
aggregate_failures('an accidentally selected empty element') do
gfm = '# Heading1'
2019-03-02 22:35:43 +05:30
html = <<~HTML
2017-09-10 17:25:29 +05:30
<h1>Heading1</h1>
<h2></h2>
2019-03-02 22:35:43 +05:30
<blockquote></blockquote>
<pre class="code highlight"></pre>
2017-09-10 17:25:29 +05:30
HTML
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
aggregate_failures('an accidentally selected other element') do
gfm = 'Test comment with **Markdown!**'
2019-03-02 22:35:43 +05:30
html = <<~HTML
2017-09-10 17:25:29 +05:30
<li class="note">
<div class="md">
<p>
Test comment with <strong>Markdown!</strong>
</p>
</div>
</li>
<li class="note"></li>
HTML
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
2017-08-17 22:00:37 +05:30
verify(
'InlineDiffFilter',
'{-Deleted text-}',
'{+Added text+}'
)
verify(
'TaskListFilter',
2019-03-02 22:35:43 +05:30
<<~GFM,
* [ ] Unchecked task
* [x] Checked task
2022-08-27 11:52:29 +05:30
* [~] Inapplicable task
* [~] Inapplicable task with ~~del~~ and <s>strike</s> embedded
2019-03-02 22:35:43 +05:30
GFM
2022-08-27 11:52:29 +05:30
<<~GFM,
2019-03-02 22:35:43 +05:30
1. [ ] Unchecked ordered task
1. [x] Checked ordered task
2022-08-27 11:52:29 +05:30
1. [~] Inapplicable ordered task
1. [~] Inapplicable ordered task with ~~del~~ and <s>strike</s> embedded
GFM
<<~GFM
* [ ] Unchecked loose list task
* [x] Checked loose list task
* [~] Inapplicable loose list task
With a paragraph
* [~] Inapplicable loose list task with ~~del~~ and <s>strike</s> embedded
With a paragraph
2019-03-02 22:35:43 +05:30
GFM
2017-08-17 22:00:37 +05:30
)
verify(
'ReferenceFilter',
# issue reference
@feat.issue.to_reference,
# full issue reference
@feat.issue.to_reference(full: true),
# issue URL
2017-09-10 17:25:29 +05:30
project_issue_url(@project, @feat.issue),
2017-08-17 22:00:37 +05:30
# issue URL with note anchor
2017-09-10 17:25:29 +05:30
project_issue_url(@project, @feat.issue, anchor: 'note_123'),
2017-08-17 22:00:37 +05:30
# issue link
2017-09-10 17:25:29 +05:30
"[Issue](#{project_issue_url(@project, @feat.issue)})",
2017-08-17 22:00:37 +05:30
# issue link with note anchor
2017-09-10 17:25:29 +05:30
"[Issue](#{project_issue_url(@project, @feat.issue, anchor: 'note_123')})"
2017-08-17 22:00:37 +05:30
)
verify(
'AutolinkFilter',
'https://example.com'
)
verify(
'TableOfContentsFilter',
2019-03-02 22:35:43 +05:30
<<~GFM,
[[_TOC_]]
# Heading 1
## Heading 2
GFM
pipeline: :wiki,
2020-06-23 00:09:42 +05:30
wiki: @project.wiki
2017-08-17 22:00:37 +05:30
)
verify(
'EmojiFilter',
':thumbsup:'
)
verify(
'ImageLinkFilter',
'![Image](https://example.com/image.png)'
)
2020-05-24 23:13:21 +05:30
verify_media_with_partial_path(
'[test.txt](/uploads/a123/image.txt)',
project_media_uri(@project, '/uploads/a123/image.txt')
)
2020-03-13 15:44:24 +05:30
verify_media_with_partial_path(
'![Image](/uploads/a123/image.png)',
project_media_uri(@project, '/uploads/a123/image.png')
)
2017-08-17 22:00:37 +05:30
verify(
'VideoLinkFilter',
'![Video](https://example.com/video.mp4)'
)
2020-03-13 15:44:24 +05:30
verify_media_with_partial_path(
'![Video](/uploads/a123/video.mp4)',
project_media_uri(@project, '/uploads/a123/video.mp4')
)
2019-12-21 20:55:43 +05:30
verify(
'AudioLinkFilter',
'![Audio](https://example.com/audio.wav)'
)
2020-03-13 15:44:24 +05:30
verify_media_with_partial_path(
'![Audio](/uploads/a123/audio.wav)',
project_media_uri(@project, '/uploads/a123/audio.wav')
)
2017-08-17 22:00:37 +05:30
verify(
'MathFilter: math as converted from GFM to HTML',
'$`c = \pm\sqrt{a^2 + b^2}`$',
# math block
2019-03-02 22:35:43 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
```math
c = \pm\sqrt{a^2 + b^2}
```
GFM
)
2021-11-18 22:05:49 +05:30
aggregate_failures('CustomEmojiFilter') do
gfm = ':custom_emoji:'
html = '<img class="emoji" src="custom_emoji.svg" title=":custom_emoji:" height="20" width="20">'
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
2017-08-17 22:00:37 +05:30
aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do
gfm = '$`c = \pm\sqrt{a^2 + b^2}`$'
2019-03-02 22:35:43 +05:30
html = <<~HTML
2017-08-17 22:00:37 +05:30
<span class="katex">
<span class="katex-mathml">
<math>
<semantics>
<mrow>
<mi>c</mi>
<mo>=</mo>
<mo>±</mo>
<msqrt>
<mrow>
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
</mrow>
</msqrt>
</mrow>
<annotation encoding="application/x-tex">c = \\pm\\sqrt{a^2 + b^2}</annotation>
</semantics>
</math>
</span>
<span class="katex-html" aria-hidden="true">
<span class="strut" style="height: 0.913389em;"></span>
<span class="strut bottom" style="height: 1.04em; vertical-align: -0.126611em;"></span>
<span class="base textstyle uncramped">
<span class="mord mathit">c</span>
<span class="mrel">=</span>
<span class="mord">±</span>
<span class="sqrt mord"><span class="sqrt-sign" style="top: -0.073389em;">
<span class="style-wrap reset-textstyle textstyle uncramped"></span>
</span>
<span class="vlist">
<span class="" style="top: 0em;">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 1em;"></span>
</span>
<span class="mord textstyle cramped">
<span class="mord">
<span class="mord mathit">a</span>
<span class="msupsub">
<span class="vlist">
<span class="" style="top: -0.289em; margin-right: 0.05em;">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 0em;"></span>
</span>
<span class="reset-textstyle scriptstyle cramped">
<span class="mord mathrm">2</span>
</span>
</span>
<span class="baseline-fix">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 0em;"></span>
</span>
</span>
</span>
</span>
</span>
<span class="mbin">+</span>
<span class="mord">
<span class="mord mathit">b</span>
<span class="msupsub">
<span class="vlist">
<span class="" style="top: -0.289em; margin-right: 0.05em;">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 0em;"></span>
</span>
<span class="reset-textstyle scriptstyle cramped">
<span class="mord mathrm">2</span>
</span>
</span>
<span class="baseline-fix">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 0em;"></span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
<span class="" style="top: -0.833389em;">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 1em;"></span>
</span>
<span class="reset-textstyle textstyle uncramped sqrt-line"></span>
</span>
<span class="baseline-fix">
<span class="fontsize-ensurer reset-size5 size5">
<span class="" style="font-size: 1em;"></span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
HTML
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
2018-03-17 18:26:18 +05:30
verify(
'MermaidFilter: mermaid as converted from GFM to HTML',
2019-03-02 22:35:43 +05:30
<<~GFM
2018-03-17 18:26:18 +05:30
```mermaid
graph TD;
A-->B;
```
GFM
)
aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do
2019-03-02 22:35:43 +05:30
gfm = <<~GFM
2018-03-17 18:26:18 +05:30
```mermaid
graph TD;
A-->B;
```
GFM
2019-03-02 22:35:43 +05:30
html = <<~HTML
2018-03-17 18:26:18 +05:30
<svg id="mermaidChart1" xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 0 87.234375 174" style="max-width:87.234375px;" class="mermaid">
<style>
.mermaid {
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
/** Section styling */
/* Grid and axis */
/* Today line */
/* Task styling */
/* Default task */
/* Specific task settings for the sections*/
/* Active task */
/* Completed task */
/* Tasks on the critical line */
}
</style>
<g>
<g class="output">
<g class="clusters"></g>
<g class="edgePaths">
<g class="edgePath" style="opacity: 1;">
<path class="path" d="M33.6171875,52L33.6171875,77L33.6171875,102" marker-end="url(#arrowhead65)" style="fill:none"></path>
<defs>
<marker id="arrowhead65" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path>
</marker>
</defs>
</g>
</g>
<g class="edgeLabels">
<g class="edgeLabel" style="opacity: 1;" transform="">
<g transform="translate(0,0)" class="label">
<foreignObject width="0" height="0">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
</g>
<g class="nodes">
<g class="node" id="A" transform="translate(33.6171875,36)" style="opacity: 1;">
<rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32"></rect>
<g class="label" transform="translate(0,0)">
<g transform="translate(-3.6171875,-6)">
<foreignObject width="7.234375" height="12">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">A</div>
</foreignObject>
</g>
</g>
</g>
<g class="node" id="B" transform="translate(33.6171875,118)" style="opacity: 1;">
<rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32">
</rect>
<g class="label" transform="translate(0,0)">
<g transform="translate(-3.6171875,-6)">
<foreignObject width="7.234375" height="12">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">B</div>
</foreignObject>
</g>
</g>
</g>
</g>
</g>
</g>
<text class="source" display="none">graph TD;
2019-03-02 22:35:43 +05:30
A--&gt;B;</text>
2018-03-17 18:26:18 +05:30
</svg>
HTML
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
2019-03-02 22:35:43 +05:30
verify(
'SuggestionFilter: suggestion as converted from GFM to HTML',
<<~GFM
```suggestion
New
And newer
```
GFM
)
aggregate_failures('SuggestionFilter: suggestion as transformed from HTML to Vue component') do
gfm = <<~GFM
```suggestion
New
And newer
```
GFM
html = <<~HTML
<div class="md-suggestion">
2021-06-08 01:23:25 +05:30
<div class="md-suggestion-header border-bottom-0 mt-2 js-suggestion-diff-header">
<div class="js-suggestion-diff-header font-weight-bold">
2019-03-02 22:35:43 +05:30
Suggested change
<a href="/gitlab/help/user/discussions/index.md#suggest-changes" aria-label="Help" class="js-help-btn">
<svg aria-hidden="true" class="s16 ic-question-o link-highlight">
<use xlink:href="/gitlab/assets/icons.svg#question-o"></use>
</svg>
</a>
</div>
<!---->
2023-03-04 22:38:38 +05:30
<button type="button" class="btn js-apply-btn">Apply suggestion</button>
2019-03-02 22:35:43 +05:30
</div>
<table class="mb-3 md-suggestion-diff js-syntax-highlight code white">
<tbody>
<tr class="line_holder old">
2023-03-04 22:38:38 +05:30
<td class="diff-line-num old_line old">9</td>
2019-03-02 22:35:43 +05:30
<td class="diff-line-num new_line old"></td>
<td class="line_content old"><span>Old
</span></td>
</tr>
<tr class="line_holder new">
<td class="diff-line-num old_line new"></td>
2023-03-04 22:38:38 +05:30
<td class="diff-line-num new_line new">9</td>
2019-03-02 22:35:43 +05:30
<td class="line_content new"><span>New
</span></td>
</tr>
<tr class="line_holder new">
<td class="diff-line-num old_line new"></td>
2023-03-04 22:38:38 +05:30
<td class="diff-line-num new_line new">10</td>
2019-03-02 22:35:43 +05:30
<td class="line_content new"><span> And newer
</span></td>
</tr>
</tbody>
</table>
</div>
HTML
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
2017-08-17 22:00:37 +05:30
verify(
'SanitizationFilter',
2019-03-02 22:35:43 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
<sub>sub</sub>
<dl>
2019-03-02 22:35:43 +05:30
<dt>dt</dt>
2017-08-17 22:00:37 +05:30
<dt>dt</dt>
<dd>dd</dd>
2019-03-02 22:35:43 +05:30
<dd>dd</dd>
<dt>dt</dt>
<dt>dt</dt>
<dd>dd</dd>
<dd>dd</dd>
2017-08-17 22:00:37 +05:30
</dl>
<kbd>kbd</kbd>
<q>q</q>
<samp>samp</samp>
<var>var</var>
2019-03-02 22:35:43 +05:30
<abbr title="HyperText &quot;Markup&quot; Language">HTML</abbr>
2017-08-17 22:00:37 +05:30
2019-03-02 22:35:43 +05:30
<details>
<summary>summary></summary>
2017-08-17 22:00:37 +05:30
2019-03-02 22:35:43 +05:30
details
</details>
2017-08-17 22:00:37 +05:30
GFM
)
verify(
'SanitizationFilter',
2019-03-02 22:35:43 +05:30
<<~GFM,
2017-08-17 22:00:37 +05:30
```
Plain text
```
GFM
2019-03-02 22:35:43 +05:30
<<~GFM,
2017-08-17 22:00:37 +05:30
```ruby
def foo
bar
end
```
GFM
2019-03-02 22:35:43 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
Foo
```js
Code goes here
```
GFM
)
verify(
'MarkdownFilter',
"Line with two spaces at the end \nto insert a linebreak",
'`code`',
'`` code with ` ticks ``',
'> Quote',
# multiline quote
2019-03-02 22:35:43 +05:30
<<~GFM,
> Multiline Quote
2017-08-17 22:00:37 +05:30
>
> With multiple paragraphs
GFM
'![Image](https://example.com/image.png)',
'# Heading with no anchor link',
'[Link](https://example.com)',
2019-03-02 22:35:43 +05:30
<<~GFM,
* List item
* List item 2
GFM
2017-08-17 22:00:37 +05:30
# multiline list item
2019-03-02 22:35:43 +05:30
<<~GFM,
* Multiline
List item
2017-08-17 22:00:37 +05:30
GFM
# nested lists
2019-03-02 22:35:43 +05:30
<<~GFM,
* Nested
* Lists
2017-08-17 22:00:37 +05:30
GFM
# list with blockquote
2019-03-02 22:35:43 +05:30
<<~GFM,
* List
2017-08-17 22:00:37 +05:30
2019-03-02 22:35:43 +05:30
> Blockquote
2017-08-17 22:00:37 +05:30
GFM
2019-03-02 22:35:43 +05:30
<<~GFM,
1. Ordered list item
1. Ordered list item 2
GFM
# multiline ordered list item
<<~GFM,
2017-08-17 22:00:37 +05:30
1. Multiline
2019-03-02 22:35:43 +05:30
Ordered list item
2017-08-17 22:00:37 +05:30
GFM
2019-03-02 22:35:43 +05:30
# nested ordered list
<<~GFM,
2017-08-17 22:00:37 +05:30
1. Nested
2019-03-02 22:35:43 +05:30
1. Ordered lists
2017-08-17 22:00:37 +05:30
GFM
2018-11-08 19:23:39 +05:30
# list item followed by an HR
2019-03-02 22:35:43 +05:30
<<~GFM,
* list item
2018-11-08 19:23:39 +05:30
2019-03-02 22:35:43 +05:30
---
2018-11-08 19:23:39 +05:30
GFM
2017-08-17 22:00:37 +05:30
'# Heading',
'## Heading',
'### Heading',
'#### Heading',
'##### Heading',
'###### Heading',
'**Bold**',
2019-03-02 22:35:43 +05:30
'*Italics*',
2022-08-27 11:52:29 +05:30
'~~Strikethrough (del)~~',
'<s>Strikethrough</s>',
2019-03-02 22:35:43 +05:30
'---',
2017-08-17 22:00:37 +05:30
# table
2019-03-02 22:35:43 +05:30
<<~GFM,
2017-08-17 22:00:37 +05:30
| Centered | Right | Left |
|:--------:|------:|------|
| Foo | Bar | **Baz** |
| Foo | Bar | **Baz** |
GFM
# table with empty heading
2020-03-13 15:44:24 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
| | x | y |
2019-03-02 22:35:43 +05:30
|--|---|---|
2017-08-17 22:00:37 +05:30
| a | 1 | 0 |
| b | 0 | 1 |
GFM
)
end
alias_method :gfm_to_html, :markdown
def verify(label, *gfms)
2019-03-02 22:35:43 +05:30
markdown_options = gfms.extract_options!
2017-08-17 22:00:37 +05:30
aggregate_failures(label) do
gfms.each do |gfm|
2019-03-02 22:35:43 +05:30
html = gfm_to_html(gfm, markdown_options).gsub(/\A&#x000A;|&#x000A;\z/, '')
2017-08-17 22:00:37 +05:30
output_gfm = html_to_gfm(html)
expect(output_gfm.strip).to eq(gfm.strip)
end
end
end
2020-03-13 15:44:24 +05:30
def project_media_uri(project, media_path)
"#{project_path(project)}#{media_path}"
end
def verify_media_with_partial_path(gfm, media_uri)
html = gfm_to_html(gfm)
output_gfm = html_to_gfm(html)
expect(output_gfm).to include(media_uri)
end
2017-08-17 22:00:37 +05:30
# Fake a `current_user` helper
def current_user
@feat.user
end
end
describe 'Copying code' do
let(:project) { create(:project, :repository) }
2021-02-22 17:27:13 +05:30
before do
2022-04-04 11:22:00 +05:30
sign_in(project.first_owner)
2021-02-22 17:27:13 +05:30
end
2017-08-17 22:00:37 +05:30
context 'from a diff' do
2018-03-17 18:26:18 +05:30
shared_examples 'copying code from a diff' do
context 'selecting one word of text' do
it 'copies as inline code' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no',
'`RuntimeError`',
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
context 'selecting one line of text' do
it 'copies as inline code' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]',
'`raise RuntimeError, "System commands must be given as an array of strings"`',
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
end
context 'selecting multiple lines of text' do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
2019-03-02 22:35:43 +05:30
<<~GFM,
2018-03-17 18:26:18 +05:30
```ruby
raise RuntimeError, "System commands must be given as an array of strings"
end
```
GFM
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'
)
end
2017-08-17 22:00:37 +05:30
end
end
2018-03-17 18:26:18 +05:30
context 'inline diff' do
before do
visit project_commit_path(project, sample_commit.id, view: 'inline')
2020-10-24 23:57:45 +05:30
wait_for_requests
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it_behaves_like 'copying code from a diff'
end
context 'parallel diff' do
before do
visit project_commit_path(project, sample_commit.id, view: 'parallel')
2020-10-24 23:57:45 +05:30
wait_for_requests
2018-03-17 18:26:18 +05:30
end
it_behaves_like 'copying code from a diff'
context 'selecting code on the left' do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
2019-03-02 22:35:43 +05:30
<<~GFM,
2018-03-17 18:26:18 +05:30
```ruby
unless cmd.is_a?(Array)
raise "System commands must be given as an array of strings"
end
```
GFM
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].left-side'
)
end
end
context 'selecting code on the right' do
it 'copies as a code block' do
verify(
'[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]',
2019-03-02 22:35:43 +05:30
<<~GFM,
2018-03-17 18:26:18 +05:30
```ruby
unless cmd.is_a?(Array)
raise RuntimeError, "System commands must be given as an array of strings"
end
```
GFM
target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].right-side'
)
end
2017-08-17 22:00:37 +05:30
end
end
end
context 'from a blob' do
before do
2017-09-10 17:25:29 +05:30
visit project_blob_path(project, File.join('master', 'files/ruby/popen.rb'))
wait_for_requests
2017-08-17 22:00:37 +05:30
end
context 'selecting one word of text' do
it 'copies as inline code' do
verify(
2022-05-07 20:08:51 +05:30
'.line[id="LC10"]',
'`end`'
2017-08-17 22:00:37 +05:30
)
end
end
context 'selecting one line of text' do
it 'copies as inline code' do
verify(
'.line[id="LC9"]',
'`raise RuntimeError, "System commands must be given as an array of strings"`'
)
end
end
context 'selecting multiple lines of text' do
it 'copies as a code block' do
verify(
'.line[id="LC9"], .line[id="LC10"]',
2020-03-13 15:44:24 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
```ruby
raise RuntimeError, "System commands must be given as an array of strings"
end
```
GFM
)
end
end
end
context 'from a GFM code block' do
before do
2017-09-10 17:25:29 +05:30
visit project_blob_path(project, File.join('markdown', 'doc/api/users.md'))
wait_for_requests
2017-08-17 22:00:37 +05:30
end
context 'selecting one word of text' do
it 'copies as inline code' do
verify(
2019-10-12 21:52:04 +05:30
'.line[id="LC27"] .nl',
2017-08-17 22:00:37 +05:30
'`"bio"`'
)
end
end
context 'selecting one line of text' do
it 'copies as inline code' do
verify(
'.line[id="LC27"]',
'`"bio": null,`'
)
end
end
context 'selecting multiple lines of text' do
it 'copies as a code block with the correct language' do
verify(
'.line[id="LC27"], .line[id="LC28"]',
2020-03-13 15:44:24 +05:30
<<~GFM
2017-08-17 22:00:37 +05:30
```json
"bio": null,
"skype": "",
```
GFM
)
end
end
end
2018-03-17 18:26:18 +05:30
def verify(selector, gfm, target: nil)
2022-05-07 20:08:51 +05:30
expect(page).to have_selector('.js-syntax-highlight')
2017-08-17 22:00:37 +05:30
html = html_for_selector(selector)
2018-03-17 18:26:18 +05:30
output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target)
2019-03-02 22:35:43 +05:30
wait_for_requests
2017-08-17 22:00:37 +05:30
expect(output_gfm.strip).to eq(gfm.strip)
end
end
def html_for_selector(selector)
2019-03-02 22:35:43 +05:30
js = <<~JS
2017-08-17 22:00:37 +05:30
(function(selector) {
var els = document.querySelectorAll(selector);
2018-03-17 18:26:18 +05:30
var htmls = [].slice.call(els).map(function(el) { return el.outerHTML; });
2017-08-17 22:00:37 +05:30
return htmls.join("\\n");
})("#{escape_javascript(selector)}")
JS
page.evaluate_script(js)
end
2018-03-17 18:26:18 +05:30
def html_to_gfm(html, transformer = 'transformGFMSelection', target: nil)
2019-03-02 22:35:43 +05:30
js = <<~JS
2017-08-17 22:00:37 +05:30
(function(html) {
2019-03-02 22:35:43 +05:30
// Setting it off so the import already starts
window.CopyAsGFM.nodeToGFM(document.createElement('div'));
2018-03-17 18:26:18 +05:30
var transformer = window.CopyAsGFM[#{transformer.inspect}];
2017-08-17 22:00:37 +05:30
var node = document.createElement('div');
2018-03-17 18:26:18 +05:30
$(html).each(function() { node.appendChild(this) });
var targetSelector = #{target.to_json};
var target;
if (targetSelector) {
target = document.querySelector(targetSelector);
}
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
node = transformer(node, target);
2017-08-17 22:00:37 +05:30
if (!node) return null;
2019-03-02 22:35:43 +05:30
window.gfmCopytestRes = null;
window.CopyAsGFM.nodeToGFM(node)
.then((res) => {
window.gfmCopytestRes = res;
});
2017-08-17 22:00:37 +05:30
})("#{escape_javascript(html)}")
JS
2019-03-02 22:35:43 +05:30
page.execute_script(js)
loop until page.evaluate_script('window.gfmCopytestRes !== null')
page.evaluate_script('window.gfmCopytestRes')
2017-08-17 22:00:37 +05:30
end
end