require 'spec_helper' describe 'Copy as GFM', feature: true, js: true do include GitlabMarkdownHelper include ActionView::Helpers::JavaScriptHelper before do @feat = MarkdownFeature.new # `markdown` helper expects a `@project` variable @project = @feat.project visit namespace_project_issue_path(@project.namespace, @project, @feat.issue) end # The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert GitLab Flavored Markdown (GFM) to HTML. # The handlers defined in app/assets/javascripts/copy_as_gfm.js.es6 consequently convert that same HTML to GFM. # To make sure these filters and handlers are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle # 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. it 'works', :aggregate_failures do verify( 'nesting', '> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**' ) verify( 'a real world example from the gitlab-ce README', <<-GFM.strip_heredoc # GitLab [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) ## Canonical source The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). ## 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/). - 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) GFM ) verify( 'InlineDiffFilter', '{-Deleted text-}', '{+Added text+}' ) verify( 'TaskListFilter', '- [ ] Unchecked task', '- [x] Checked task', '1. [ ] Unchecked numbered task', '1. [x] Checked numbered task' ) verify( 'ReferenceFilter', # issue reference @feat.issue.to_reference, # full issue reference @feat.issue.to_reference(full: true), # issue URL namespace_project_issue_url(@project.namespace, @project, @feat.issue), # issue URL with note anchor namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123'), # issue link "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue)})", # issue link with note anchor "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123')})", ) verify( 'AutolinkFilter', 'https://example.com' ) verify( 'TableOfContentsFilter', '[[_TOC_]]' ) verify( 'EmojiFilter', ':thumbsup:' ) verify( 'ImageLinkFilter', '![Image](https://example.com/image.png)' ) verify( 'VideoLinkFilter', '![Video](https://example.com/video.mp4)' ) verify( 'MathFilter: math as converted from GFM to HTML', '$`c = \pm\sqrt{a^2 + b^2}`$', # math block <<-GFM.strip_heredoc ```math c = \pm\sqrt{a^2 + b^2} ``` GFM ) aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do gfm = '$`c = \pm\sqrt{a^2 + b^2}`$' html = <<-HTML.strip_heredoc <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 verify( 'SanitizationFilter', <<-GFM.strip_heredoc <a name="named-anchor"></a> <sub>sub</sub> <dl> <dt>dt</dt> <dd>dd</dd> </dl> <kbd>kbd</kbd> <q>q</q> <samp>samp</samp> <var>var</var> <ruby>ruby</ruby> <rt>rt</rt> <rp>rp</rp> <abbr>abbr</abbr> <summary>summary</summary> <details>details</details> GFM ) verify( 'SanitizationFilter', <<-GFM.strip_heredoc, ``` Plain text ``` GFM <<-GFM.strip_heredoc, ```ruby def foo bar end ``` GFM <<-GFM.strip_heredoc Foo This is an example of GFM ```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 <<-GFM.strip_heredoc, > Multiline > Quote > > With multiple paragraphs GFM '![Image](https://example.com/image.png)', '# Heading with no anchor link', '[Link](https://example.com)', '- List item', # multiline list item <<-GFM.strip_heredoc, - Multiline List item GFM # nested lists <<-GFM.strip_heredoc, - Nested - Lists GFM # list with blockquote <<-GFM.strip_heredoc, - List > Blockquote GFM '1. Numbered list item', # multiline numbered list item <<-GFM.strip_heredoc, 1. Multiline Numbered list item GFM # nested numbered list <<-GFM.strip_heredoc, 1. Nested 1. Numbered lists GFM '# Heading', '## Heading', '### Heading', '#### Heading', '##### Heading', '###### Heading', '**Bold**', '_Italics_', '~~Strikethrough~~', '2^2', '-----', # table <<-GFM.strip_heredoc, | Centered | Right | Left | |:--------:|------:|------| | Foo | Bar | **Baz** | | Foo | Bar | **Baz** | GFM # table with empty heading <<-GFM.strip_heredoc, | | x | y | |---|---|---| | a | 1 | 0 | | b | 0 | 1 | GFM ) end alias_method :gfm_to_html, :markdown def html_to_gfm(html) js = <<-JS.strip_heredoc (function(html) { var node = document.createElement('div'); node.innerHTML = html; return window.gl.CopyAsGFM.nodeToGFM(node); })("#{escape_javascript(html)}") JS page.evaluate_script(js) end def verify(label, *gfms) aggregate_failures(label) do gfms.each do |gfm| html = gfm_to_html(gfm) output_gfm = html_to_gfm(html) expect(output_gfm.strip).to eq(gfm.strip) end end end # Fake a `current_user` helper def current_user @feat.user end end