debian-mirror-gitlab/spec/lib/gitlab/asciidoc_spec.rb

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

863 lines
29 KiB
Ruby
Raw Normal View History

2019-10-12 21:52:04 +05:30
# frozen_string_literal: true
2015-09-11 14:41:01 +05:30
require 'spec_helper'
require 'nokogiri'
module Gitlab
2023-07-09 08:55:56 +05:30
RSpec.describe Asciidoc, feature_category: :wiki do
2019-09-04 21:01:54 +05:30
include FakeBlobHelpers
before do
allow_any_instance_of(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults)
end
2015-09-11 14:41:01 +05:30
2022-03-02 08:16:31 +05:30
context "without project" do
let(:input) { '<b>ascii</b>' }
let(:context) { {} }
let(:html) { 'H<sub>2</sub>O' }
it "converts the input using Asciidoctor and default options" do
expected_asciidoc_opts = {
safe: :secure,
backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
extensions: be_a(Proc)
}
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
expect(render(input, context)).to eq(html)
end
2015-09-11 14:41:01 +05:30
2022-03-02 08:16:31 +05:30
context "with asciidoc_opts" do
it "merges the options with default ones" do
2015-09-11 14:41:01 +05:30
expected_asciidoc_opts = {
2017-08-17 22:00:37 +05:30
safe: :secure,
backend: :gitlab_html5,
2021-02-22 17:27:13 +05:30
attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
2019-09-04 21:01:54 +05:30
extensions: be_a(Proc)
2015-09-11 14:41:01 +05:30
}
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
2022-03-02 08:16:31 +05:30
render(input, context)
2017-08-17 22:00:37 +05:30
end
2022-03-02 08:16:31 +05:30
end
2017-08-17 22:00:37 +05:30
2022-03-02 08:16:31 +05:30
context "with requested path" do
input = <<~ADOC
Document name: {docname}.
ADOC
it "ignores {docname} when not available" do
expect(render(input, {})).to include(input.strip)
end
[
['/', '', 'root'],
['README', 'README', 'just a filename'],
['doc/api/', '', 'a directory'],
['doc/api/README.adoc', 'README', 'a complete path']
].each do |path, basename, desc|
it "sets {docname} for #{desc}" do
expect(render(input, { requested_path: path })).to include(": #{basename}.")
end
end
end
2020-04-22 19:07:51 +05:30
2022-03-02 08:16:31 +05:30
context "XSS" do
items = {
'link with extra attribute' => {
input: 'link:mylink"onmouseover="alert(1)[Click Here]',
output: "<div>\n<p><a href=\"mylink\">Click Here</a></p>\n</div>"
},
'link with unsafe scheme' => {
input: 'link:data://danger[Click Here]',
output: "<div>\n<p><a>Click Here</a></p>\n</div>"
},
'image with onerror' => {
input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]',
2022-07-16 23:28:13 +05:30
output: "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt='Alt text\" onerror=\"alert(7)' decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
2022-03-02 08:16:31 +05:30
}
}
2017-08-17 22:00:37 +05:30
2022-03-02 08:16:31 +05:30
items.each do |name, data|
it "does not convert dangerous #{name} into HTML" do
expect(render(data[:input], context)).to include(data[:output])
2017-08-17 22:00:37 +05:30
end
end
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
# `stub_feature_flags method` runs AFTER declaration of `items` above.
# So the spec in its current implementation won't pass.
# Move this test back to the items hash when removing `use_cmark_renderer` feature flag.
it "does not convert dangerous fenced code with inline script into HTML" do
input = '```mypre"><script>alert(3)</script>'
2023-01-13 00:05:48 +05:30
output = <<~HTML
<div>
<div>
2023-07-09 08:55:56 +05:30
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-canonical-lang="mypre" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code></code></pre>
2023-01-13 00:05:48 +05:30
<copy-code></copy-code>
</div>
</div>
</div>
HTML
2022-03-02 08:16:31 +05:30
2023-01-13 00:05:48 +05:30
expect(render(input, context)).to include(output.strip)
2022-03-02 08:16:31 +05:30
end
it 'does not allow locked attributes to be overridden' do
2021-04-01 16:36:13 +05:30
input = <<~ADOC
2022-03-02 08:16:31 +05:30
{counter:max-include-depth:1234}
<|-- {max-include-depth}
2021-04-01 16:36:13 +05:30
ADOC
2022-03-02 08:16:31 +05:30
expect(render(input, {})).not_to include('1234')
end
end
2017-08-17 22:00:37 +05:30
2022-03-02 08:16:31 +05:30
context "images" do
it "does lazy load and link image" do
input = 'image:https://localhost.com/image.png[]'
2022-07-16 23:28:13 +05:30
output = "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output)
2019-12-26 22:10:19 +05:30
end
2022-03-02 08:16:31 +05:30
it "does not automatically link image if link is explicitly defined" do
input = 'image:https://localhost.com/image.png[link=https://gitlab.com]'
2022-07-16 23:28:13 +05:30
output = "<div>\n<p><span><a href=\"https://gitlab.com\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output)
end
end
2019-12-26 22:10:19 +05:30
2022-03-02 08:16:31 +05:30
context 'with admonition' do
it 'preserves classes' do
input = <<~ADOC
NOTE: An admonition paragraph, like this note, grabs the readers attention.
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div class="admonitionblock">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td>
An admonition paragraph, like this note, grabs the readers attention.
</td>
</tr>
</table>
</div>
HTML
expect(render(input, context)).to include(output.strip)
end
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with passthrough' do
it 'removes non heading ids' do
input = <<~ADOC
++++
<h2 id="foo">Title</h2>
++++
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<h2>Title</h2>
HTML
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
it 'removes non footnote def ids' do
input = <<~ADOC
++++
<div id="def">Footnote definition</div>
++++
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>Footnote definition</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
it 'removes non footnote ref ids' do
input = <<~ADOC
++++
<a id="ref">Footnote reference</a>
++++
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<a>Footnote reference</a>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with footnotes' do
it 'preserves ids and links' do
input = <<~ADOC
This paragraph has a footnote.footnote:[This is the text of the footnote.]
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
</div>
<div>
<hr>
<div id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. This is the text of the footnote.
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
end
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with section anchors' do
it 'preserves ids and links' do
input = <<~ADOC
= Title
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
== First section
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
This is the first section.
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
== Second section
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
This is the second section.
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
== Thunder !
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
This is the third section.
ADOC
output = <<~HTML
<h1>Title</h1>
<div>
<h2 id="user-content-first-section">
<a class="anchor" href="#user-content-first-section"></a>First section</h2>
<div>
<div>
<p>This is the first section.</p>
</div>
</div>
</div>
<div>
<h2 id="user-content-second-section">
<a class="anchor" href="#user-content-second-section"></a>Second section</h2>
<div>
<div>
<p>This is the second section.</p>
</div>
</div>
</div>
<div>
<h2 id="user-content-thunder">
<a class="anchor" href="#user-content-thunder"></a>Thunder ⚡ !</h2>
<div>
<div>
<p>This is the third section.</p>
</div>
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
context 'with xrefs' do
it 'preserves ids' do
input = <<~ADOC
Learn how to xref:cross-references[use cross references].
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
[[cross-references]]A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).
ADOC
output = <<~HTML
<div>
<p>Learn how to <a href="#cross-references">use cross references</a>.</p>
</div>
<div>
<p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).</p>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2021-02-22 17:27:13 +05:30
end
2022-03-02 08:16:31 +05:30
end
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
context 'with checklist' do
it 'preserves classes' do
input = <<~ADOC
* [x] checked
* [ ] not checked
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<ul class="checklist">
<li>
<p><i class="fa fa-check-square-o"></i> checked</p>
</li>
<li>
<p><i class="fa fa-square-o"></i> not checked</p>
</li>
</ul>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with marks' do
it 'preserves classes' do
input = <<~ADOC
Werewolves are allergic to #cassia cinnamon#.
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
Did the werewolves read the [.small]#small print#?
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
Where did all the [.underline.small]#cores# run off to?
We need [.line-through]#ten# make that twenty VMs.
[.big]##O##nce upon an infinite loop.
ADOC
output = <<~HTML
<div>
<p>Werewolves are allergic to <mark>cassia cinnamon</mark>.</p>
</div>
<div>
<p>Did the werewolves read the <span class="small">small print</span>?</p>
</div>
<div>
<p>Where did all the <span class="underline small">cores</span> run off to?</p>
</div>
<div>
<p>We need <span class="line-through">ten</span> make that twenty VMs.</p>
</div>
<div>
<p><span class="big">O</span>nce upon an infinite loop.</p>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with fenced block' do
it 'highlights syntax' do
input = <<~ADOC
```js
console.log('hello world')
```
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<div>
<div class="gl-relative markdown-code-block js-markdown-code">
2023-07-09 08:55:56 +05:30
<pre data-canonical-lang="js" 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="nf">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>
2022-03-02 08:16:31 +05:30
<copy-code></copy-code>
</div>
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with listing block' do
it 'highlights syntax' do
input = <<~ADOC
[source,c++]
.class.cpp
----
#include <stdio.h>
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
for (int i = 0; i < 5; i++) {
std::cout<<"*"<<std::endl;
}
----
ADOC
output = <<~HTML
<div>
<div>class.cpp</div>
<div>
<div class="gl-relative markdown-code-block js-markdown-code">
2023-07-09 08:55:56 +05:30
<pre data-canonical-lang="c++" class="code highlight js-syntax-highlight language-cpp" lang="cpp" v-pre="true"><code><span id="LC1" class="line" lang="cpp"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span></span>
2022-03-02 08:16:31 +05:30
<span id="LC2" class="line" lang="cpp"></span>
<span id="LC3" class="line" lang="cpp"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span></span>
<span id="LC4" class="line" lang="cpp"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="o">&lt;&lt;</span><span class="s">"*"</span><span class="o">&lt;&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span></span>
<span id="LC5" class="line" lang="cpp"><span class="p">}</span></span></code></pre>
<copy-code></copy-code>
</div>
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'with stem block' do
it 'does not apply syntax highlighting' do
input = <<~ADOC
[stem]
++++
\sqrt{4} = 2
++++
ADOC
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
output = "<div>\n<div>\n\\$ qrt{4} = 2\\$\n</div>\n</div>"
expect(render(input, context)).to include(output)
2019-09-30 21:07:59 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-30 21:07:59 +05:30
2022-03-02 08:16:31 +05:30
context 'external links' do
it 'adds the `rel` attribute to the link' do
output = render('link:https://google.com[Google]', context)
2017-08-17 22:00:37 +05:30
2022-03-02 08:16:31 +05:30
expect(output).to include('rel="nofollow noreferrer noopener"')
2015-09-11 14:41:01 +05:30
end
2022-03-02 08:16:31 +05:30
end
2017-09-10 17:25:29 +05:30
2022-03-02 08:16:31 +05:30
context 'LaTex code' do
it 'adds class js-render-math to the output' do
input = <<~MD
:stem: latexmath
2017-09-10 17:25:29 +05:30
2022-03-02 08:16:31 +05:30
[stem]
++++
\sqrt{4} = 2
++++
2017-09-10 17:25:29 +05:30
2022-03-02 08:16:31 +05:30
another part
[latexmath]
++++
\beta_x \gamma
++++
stem:[2+2] is 4
MD
2023-07-09 08:55:56 +05:30
expect(render(input, context)).to include('<pre data-math-style="display" class="code math js-render-math" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">eta_x gamma</span></code></pre>')
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include('<p><code data-math-style="inline" class="code math js-render-math">2+2</code> is 4</p>')
2017-09-10 17:25:29 +05:30
end
2022-03-02 08:16:31 +05:30
end
2018-03-27 19:54:05 +05:30
2022-03-02 08:16:31 +05:30
context 'outfilesuffix' do
it 'defaults to adoc' do
output = render("Inter-document reference <<README.adoc#>>", context)
2018-03-27 19:54:05 +05:30
2022-03-02 08:16:31 +05:30
expect(output).to include("a href=\"README.adoc\"")
2018-03-27 19:54:05 +05:30
end
2022-03-02 08:16:31 +05:30
end
2020-10-24 23:57:45 +05:30
2022-03-02 08:16:31 +05:30
context 'with mermaid diagrams' do
it 'adds class js-render-mermaid to the output' do
input = <<~MD
[mermaid]
....
graph LR
A[Square Rect] -- Link text --> B((Circle))
A --> C(Round Rect)
B --> D{Rhombus}
C --> D
....
MD
output = <<~HTML
<pre data-mermaid-style="display" class="js-render-mermaid">graph LR
A[Square Rect] -- Link text --&gt; B((Circle))
A --&gt; C(Round Rect)
B --&gt; D{Rhombus}
C --&gt; D</pre>
HTML
expect(render(input, context)).to include(output.strip)
2020-10-24 23:57:45 +05:30
end
2022-03-02 08:16:31 +05:30
it 'applies subs in diagram block' do
input = <<~MD
:class-name: AveryLongClass
2020-10-24 23:57:45 +05:30
2022-03-02 08:16:31 +05:30
[mermaid,subs=+attributes]
....
classDiagram
Class01 <|-- {class-name} : Cool
....
MD
2020-10-24 23:57:45 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<pre data-mermaid-style="display" class="js-render-mermaid">classDiagram
Class01 &lt;|-- AveryLongClass : Cool</pre>
HTML
2020-10-24 23:57:45 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output.strip)
2020-10-24 23:57:45 +05:30
end
2022-03-02 08:16:31 +05:30
end
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
context 'with Kroki enabled' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:kroki_enabled).and_return(true)
allow_any_instance_of(ApplicationSetting).to receive(:kroki_url).and_return('https://kroki.io')
end
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
it 'converts a graphviz diagram to image' do
input = <<~ADOC
[graphviz]
....
digraph G {
Hello->World
}
....
ADOC
2021-02-22 17:27:13 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<div>
2022-07-16 23:28:13 +05:30
<a class="no-attachment-icon" href="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" decoding="async" class="lazy" data-src="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka"></a>
2022-03-02 08:16:31 +05:30
</div>
</div>
HTML
2021-03-11 19:13:27 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output.strip)
end
2021-03-11 19:13:27 +05:30
2022-03-02 08:16:31 +05:30
it 'does not convert a blockdiag diagram to image' do
input = <<~ADOC
[blockdiag]
....
blockdiag {
Kroki -> generates -> "Block diagrams";
Kroki -> is -> "very easy!";
Kroki [color = "greenyellow"];
"Block diagrams" [color = "pink"];
"very easy!" [color = "orange"];
}
....
ADOC
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<div>
<pre>blockdiag {
Kroki -&gt; generates -&gt; "Block diagrams";
Kroki -&gt; is -&gt; "very easy!";
Kroki [color = "greenyellow"];
"Block diagrams" [color = "pink"];
"very easy!" [color = "orange"];
}</pre>
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
end
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
it 'does not allow kroki-plantuml-include to be overridden' do
input = <<~ADOC
2023-01-13 00:05:48 +05:30
[plantuml, test="{counter:kroki-plantuml-include:README.md}", format="png"]
2022-03-02 08:16:31 +05:30
....
class BlockProcessor
2021-12-11 22:18:48 +05:30
2022-03-02 08:16:31 +05:30
BlockProcessor <|-- {counter:kroki-plantuml-include}
....
ADOC
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<div>
2023-01-13 00:05:48 +05:30
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\"></a>
2022-03-02 08:16:31 +05:30
</div>
</div>
HTML
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, {})).to include(output.strip)
2021-04-01 16:36:13 +05:30
end
2022-03-02 08:16:31 +05:30
it 'does not allow kroki-server-url to be overridden' do
input = <<~ADOC
[plantuml, test="{counter:kroki-server-url:evilsite}", format="png"]
....
class BlockProcessor
2021-04-01 16:36:13 +05:30
2022-03-02 08:16:31 +05:30
BlockProcessor
....
ADOC
2021-12-11 22:18:48 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, {})).not_to include('evilsite')
2021-04-01 16:36:13 +05:30
end
2021-03-11 19:13:27 +05:30
end
2022-03-02 08:16:31 +05:30
context 'with Kroki and BlockDiag (additional format) enabled' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:kroki_enabled).and_return(true)
allow_any_instance_of(ApplicationSetting).to receive(:kroki_url).and_return('https://kroki.io')
allow_any_instance_of(ApplicationSetting).to receive(:kroki_formats_blockdiag).and_return(true)
2021-03-11 19:13:27 +05:30
end
2022-03-02 08:16:31 +05:30
it 'converts a blockdiag diagram to image' do
input = <<~ADOC
[blockdiag]
....
blockdiag {
Kroki -> generates -> "Block diagrams";
Kroki -> is -> "very easy!";
Kroki [color = "greenyellow"];
"Block diagrams" [color = "pink"];
"very easy!" [color = "orange"];
}
....
ADOC
2021-03-11 19:13:27 +05:30
2022-03-02 08:16:31 +05:30
output = <<~HTML
<div>
<div>
2022-07-16 23:28:13 +05:30
<a class="no-attachment-icon" href="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" decoding="async" class="lazy" data-src="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w=="></a>
2022-03-02 08:16:31 +05:30
</div>
</div>
HTML
2015-09-11 14:41:01 +05:30
2022-03-02 08:16:31 +05:30
expect(render(input, context)).to include(output.strip)
end
end
end
2020-10-24 23:57:45 +05:30
2022-03-02 08:16:31 +05:30
context 'with project' do
let(:context) do
{
2022-10-11 01:57:18 +05:30
commit: commit,
project: project,
ref: ref,
2022-03-02 08:16:31 +05:30
requested_path: requested_path
}
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
let(:commit) { project.commit(ref) }
let(:project) { create(:project, :repository) }
let(:ref) { 'asciidoc' }
let(:requested_path) { '/' }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'include directive' do
subject(:output) { render(input, context) }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
let(:input) { "Include this:\n\ninclude::#{include_path}[]" }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
before do
current_file = requested_path
current_file += 'README.adoc' if requested_path.end_with? '/'
2020-02-01 01:16:34 +05:30
2022-03-02 08:16:31 +05:30
create_file(current_file, "= AsciiDoc\n")
end
def many_includes(target)
Array.new(10, "include::#{target}[]").join("\n")
end
context 'cyclic imports' do
before do
create_file('doc/api/a.adoc', many_includes('b.adoc'))
create_file('doc/api/b.adoc', many_includes('a.adoc'))
2020-02-01 01:16:34 +05:30
end
2022-03-02 08:16:31 +05:30
let(:include_path) { 'a.adoc' }
let(:requested_path) { 'doc/api/README.md' }
2020-02-01 01:16:34 +05:30
2022-03-02 08:16:31 +05:30
it 'completes successfully' do
is_expected.to include('<p>Include this:</p>')
2020-02-01 01:16:34 +05:30
end
2022-03-02 08:16:31 +05:30
end
2020-02-01 01:16:34 +05:30
2022-03-02 08:16:31 +05:30
context 'with path to non-existing file' do
let(:include_path) { 'not-exists.adoc' }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
it 'renders Unresolved directive placeholder' do
is_expected.to include("<strong>[ERROR: include::#{include_path}[] - unresolved directive]</strong>")
end
end
2019-09-04 21:01:54 +05:30
2023-03-04 22:38:38 +05:30
shared_examples 'invalid include' do
2022-03-02 08:16:31 +05:30
let(:include_path) { 'dk.png' }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
before do
allow(project.repository).to receive(:blob_at).and_return(blob)
2019-09-04 21:01:54 +05:30
end
2022-03-02 08:16:31 +05:30
it 'does not read the blob' do
expect(blob).not_to receive(:data)
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
it 'renders Unresolved directive placeholder' do
is_expected.to include("<strong>[ERROR: include::#{include_path}[] - unresolved directive]</strong>")
2019-09-04 21:01:54 +05:30
end
2022-03-02 08:16:31 +05:30
end
2020-01-01 13:55:28 +05:30
2022-03-02 08:16:31 +05:30
context 'with path to a binary file' do
let(:blob) { fake_blob(path: 'dk.png', binary: true) }
2019-09-04 21:01:54 +05:30
2023-03-04 22:38:38 +05:30
include_examples 'invalid include'
2022-03-02 08:16:31 +05:30
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'with path to file in external storage' do
let(:blob) { fake_blob(path: 'dk.png', lfs: true) }
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
project.update_attribute(:lfs_enabled, true)
2019-09-04 21:01:54 +05:30
end
2023-03-04 22:38:38 +05:30
include_examples 'invalid include'
2022-03-02 08:16:31 +05:30
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'with path to a textual file' do
let(:include_path) { 'sample.adoc' }
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
before do
create_file(file_path, "Content from #{include_path}")
end
2023-03-04 22:38:38 +05:30
shared_examples 'valid include' do
2022-03-02 08:16:31 +05:30
[
['/doc/sample.adoc', 'doc/sample.adoc', 'absolute path'],
['sample.adoc', 'doc/api/sample.adoc', 'relative path'],
['./sample.adoc', 'doc/api/sample.adoc', 'relative path with leading ./'],
['../sample.adoc', 'doc/sample.adoc', 'relative path to a file up one directory'],
['../../sample.adoc', 'sample.adoc', 'relative path for a file up multiple directories']
].each do |include_path_, file_path_, desc|
context "the file is specified by #{desc}" do
let(:include_path) { include_path_ }
let(:file_path) { file_path_ }
it 'includes content of the file' do
is_expected.to include('<p>Include this:</p>')
is_expected.to include("<p>Content from #{include_path}</p>")
2019-09-04 21:01:54 +05:30
end
end
end
2022-03-02 08:16:31 +05:30
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'when requested path is a file in the repo' do
let(:requested_path) { 'doc/api/README.adoc' }
2019-09-04 21:01:54 +05:30
2023-03-04 22:38:38 +05:30
include_examples 'valid include'
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'without a commit (only ref)' do
let(:commit) { nil }
2020-01-01 13:55:28 +05:30
2023-03-04 22:38:38 +05:30
include_examples 'valid include'
2019-09-04 21:01:54 +05:30
end
2022-03-02 08:16:31 +05:30
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'when requested path is a directory in the repo' do
let(:requested_path) { 'doc/api/' }
2019-09-04 21:01:54 +05:30
2023-03-04 22:38:38 +05:30
include_examples 'valid include'
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'without a commit (only ref)' do
let(:commit) { nil }
2020-01-01 13:55:28 +05:30
2023-03-04 22:38:38 +05:30
include_examples 'valid include'
2019-09-04 21:01:54 +05:30
end
end
2022-03-02 08:16:31 +05:30
end
2020-03-13 15:44:24 +05:30
2022-03-02 08:16:31 +05:30
context 'when repository is passed into the context' do
let(:wiki_repo) { project.wiki.repository }
let(:include_path) { 'wiki_file.adoc' }
2020-03-13 15:44:24 +05:30
2022-03-02 08:16:31 +05:30
before do
project.create_wiki
context.merge!(repository: wiki_repo)
end
context 'when the file exists' do
2020-03-13 15:44:24 +05:30
before do
2022-03-02 08:16:31 +05:30
create_file(include_path, 'Content from wiki', repository: wiki_repo)
2020-03-13 15:44:24 +05:30
end
2022-03-02 08:16:31 +05:30
it { is_expected.to include('<p>Content from wiki</p>') }
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'when the file does not exist' do
2022-08-27 11:52:29 +05:30
it { is_expected.to include("[ERROR: include::#{include_path}[] - unresolved directive]") }
2022-03-02 08:16:31 +05:30
end
end
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
context 'recursive includes with relative paths' do
let(:input) do
<<~ADOC
Source: requested file
include::doc/README.adoc[]
include::license.adoc[]
ADOC
2019-09-04 21:01:54 +05:30
end
2022-03-02 08:16:31 +05:30
before do
create_file 'doc/README.adoc', <<~ADOC
Source: doc/README.adoc
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
include::../license.adoc[]
2019-09-04 21:01:54 +05:30
2022-03-02 08:16:31 +05:30
include::api/hello.adoc[]
ADOC
create_file 'license.adoc', <<~ADOC
Source: license.adoc
ADOC
create_file 'doc/api/hello.adoc', <<~ADOC
Source: doc/api/hello.adoc
include::./common.adoc[]
ADOC
create_file 'doc/api/common.adoc', <<~ADOC
Source: doc/api/common.adoc
ADOC
2019-09-04 21:01:54 +05:30
end
2022-03-02 08:16:31 +05:30
it 'includes content of the included files recursively' do
expect(output.gsub(/<[^>]+>/, '').gsub(/\n\s*/, "\n").strip).to eq <<~ADOC.strip
Source: requested file
Source: doc/README.adoc
Source: license.adoc
Source: doc/api/hello.adoc
Source: doc/api/common.adoc
Source: license.adoc
ADOC
2019-09-04 21:01:54 +05:30
end
end
2022-03-02 08:16:31 +05:30
def create_file(path, content, repository: project.repository)
repository.create_file(project.creator, path, content,
message: "Add #{path}", branch_name: 'asciidoc')
end
2019-09-04 21:01:54 +05:30
end
end
2023-04-23 21:23:45 +05:30
def render(...)
described_class.render(...)
2015-09-11 14:41:01 +05:30
end
end
end