diff options
author | Robert Speicher <rspeicher@gmail.com> | 2015-07-22 21:22:57 -0400 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2015-07-28 18:56:27 -0400 |
commit | 05f9a6a9c44c094d281d3dc8d80eb30c4e7dff27 (patch) | |
tree | faa30eb8ab5b30f649794254fe7e4736098d1ff8 /spec | |
parent | 0673ca36812ac3f709032938b77e5ab6e1d63885 (diff) | |
download | gitlab-ce-05f9a6a9c44c094d281d3dc8d80eb30c4e7dff27.tar.gz |
Update Markdown feature to allow for multiple pipelines
Diffstat (limited to 'spec')
-rw-r--r-- | spec/features/markdown_spec.rb | 216 | ||||
-rw-r--r-- | spec/support/matchers/markdown_matchers.rb | 156 |
2 files changed, 209 insertions, 163 deletions
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 46814fdfb8e..ddef2317e1c 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -17,16 +17,8 @@ require 'erb' # -> Post-process HTML # -> `gfm_with_options` helper # -> HTML::Pipeline -# -> Sanitize -# -> RelativeLink -# -> Emoji -# -> Table of Contents -# -> Autolinks -# -> Rinku (http, https, ftp) -# -> Other schemes -# -> ExternalLink -# -> References -# -> TaskList +# -> SanitizationFilter +# -> Other filters, depending on pipeline # -> `html_safe` # -> Template # @@ -35,6 +27,7 @@ require 'erb' describe 'GitLab Markdown', feature: true do include Capybara::Node::Matchers include GitlabMarkdownHelper + include MarkdownMatchers # Let's only parse this thing once before(:all) do @@ -42,50 +35,37 @@ describe 'GitLab Markdown', feature: true do # `gfm_with_options` depends on a `@project` variable @project = @feat.project - - @html = markdown(@feat.raw_markdown) end after(:all) do @feat.teardown end - def doc - @doc ||= Nokogiri::HTML::DocumentFragment.parse(@html) + def doc(html = @html) + Nokogiri::HTML::DocumentFragment.parse(html) end - # Given a header ID, goes to that element's parent (the header itself), then - # its next sibling element (the body). - def get_section(id) - doc.at_css("##{id}").parent.next_element + # Sometimes it can be useful to see the parsed output of the Markdown document + # for debugging. Call this method to write the output to + # `tmp/capybara/<filename>.html`. + def write_markdown(filename = 'markdown_spec') + File.open(Rails.root.join("tmp/capybara/#{filename}.html"), 'w') do |file| + file.puts @html + end end - # Sometimes it can be useful to see the parsed output of the Markdown document - # for debugging. Uncomment this block to write the output to - # tmp/capybara/markdown_spec.html. - # - # it 'writes to a file' do - # File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file| - # file.puts @html - # end - # end - - describe 'Redcarpet extensions' do - describe 'No Intra Emphasis' do + # Shared behavior that all pipelines should exhibit + shared_examples 'all pipelines' do + describe 'Redcarpet extensions' do it 'does not parse emphasis inside of words' do - body = get_section('no-intra-emphasis') - expect(body.to_html).not_to match('foo<em>bar</em>baz') + expect(doc.to_html).not_to match('foo<em>bar</em>baz') end - end - describe 'Tables' do it 'parses table Markdown' do - body = get_section('tables') - aggregate_failures do - expect(body).to have_selector('th:contains("Header")') - expect(body).to have_selector('th:contains("Row")') - expect(body).to have_selector('th:contains("Example")') + expect(doc).to have_selector('th:contains("Header")') + expect(doc).to have_selector('th:contains("Row")') + expect(doc).to have_selector('th:contains("Example")') end end @@ -93,36 +73,23 @@ describe 'GitLab Markdown', feature: true do expect(doc.at_css('td:contains("Baz")').children.to_html). to eq '<strong>Baz</strong>' end - end - describe 'Fenced Code Blocks' do it 'parses fenced code blocks' do aggregate_failures do expect(doc).to have_selector('pre.code.highlight.white.c') expect(doc).to have_selector('pre.code.highlight.white.python') end end - end - describe 'Strikethrough' do it 'parses strikethroughs' do expect(doc).to have_selector(%{del:contains("and this text doesn't")}) end - end - describe 'Superscript' do it 'parses superscript' do - body = get_section('superscript') - - aggregate_failures do - expect(body.to_html).to match('1<sup>st</sup>') - expect(body.to_html).to match('2<sup>nd</sup>') - end + expect(doc).to have_selector('sup', count: 2) end end - end - describe 'HTML::Pipeline' do describe 'SanitizationFilter' do it 'permits b elements' do expect(doc).to have_selector('b:contains("b tag")') @@ -207,133 +174,56 @@ describe 'GitLab Markdown', feature: true do end end - describe 'EmojiFilter' do - it 'parses Emoji' do - expect(doc).to have_selector('img.emoji', count: 10) - end - end - - describe 'TableOfContentsFilter' do - it 'creates anchors inside header elements' do - aggregate_failures do - expect(doc).to have_selector('h1 a#gitlab-markdown') - expect(doc).to have_selector('h2 a#markdown') - expect(doc).to have_selector('h3 a#autolinkfilter') - end - end - end - - describe 'AutolinkFilter' do - def body - get_section('autolinkfilter').next_element - end - - # Override Capybara's `have_link` matcher to simplify our use case - def have_link(link) - super(link, href: link) - end - - it 'autolinks http://' do - expect(body).to have_link('http://about.gitlab.com/') - end - - it 'autolinks https://' do - expect(body).to have_link('https://google.com/') - end - - it 'autolinks ftp://' do - expect(body).to have_link('ftp://ftp.us.debian.org/debian/') - end - - it 'autolinks smb://' do - expect(body).to have_link('smb://foo/bar/baz') - end - - it 'autolinks irc://' do - expect(body).to have_link('irc://irc.freenode.net/git') - end - - it 'autolinks short, invalid URLs' do - expect(body).to have_link('http://localhost:3000') - end - - %w(code a kbd).each do |elem| - it "ignores links inside '#{elem}' element" do - expect(body).not_to have_selector("#{elem} a") - end - end - end - describe 'ExternalLinkFilter' do - let(:links) { get_section('externallinkfilter').next_element } - it 'adds nofollow to external link' do - expect(links.css('a').first.to_html).to match 'nofollow' + link = doc.at_css('a:contains("Google")') + expect(link.attr('rel')).to match 'nofollow' end it 'ignores internal link' do - expect(links.css('a').last.to_html).not_to match 'nofollow' + link = doc.at_css('a:contains("GitLab Root")') + expect(link.attr('rel')).not_to match 'nofollow' end end + end - describe 'ReferenceFilter' do - it 'handles references in headers' do - header = doc.at_css('#reference-filters-eg-1').parent - - expect(header.css('a').size).to eq 2 - end - - it "handles references in Markdown" do - body = get_section('reference-filters-eg-1') - expect(body).to have_selector('em a.gfm-merge_request', count: 1) - end - - it 'parses user references' do - body = get_section('userreferencefilter') - expect(body).to have_selector('a.gfm.gfm-project_member', count: 3) - end + context 'default pipeline' do + before(:all) do + @html = markdown(@feat.raw_markdown) + end - it 'parses issue references' do - body = get_section('issuereferencefilter') - expect(body).to have_selector('a.gfm.gfm-issue', count: 2) - end + it_behaves_like 'all pipelines' - it 'parses merge request references' do - body = get_section('mergerequestreferencefilter') - expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2) - end + it 'includes RelativeLinkFilter' do + expect(doc).to parse_relative_links + end - it 'parses snippet references' do - body = get_section('snippetreferencefilter') - expect(body).to have_selector('a.gfm.gfm-snippet', count: 2) - end + it 'includes EmojiFilter' do + expect(doc).to parse_emoji + end - it 'parses commit range references' do - body = get_section('commitrangereferencefilter') - expect(body).to have_selector('a.gfm.gfm-commit_range', count: 2) - end + it 'includes TableOfContentsFilter' do + expect(doc).to create_header_links + end - it 'parses commit references' do - body = get_section('commitreferencefilter') - expect(body).to have_selector('a.gfm.gfm-commit', count: 2) - end + it 'includes AutolinkFilter' do + expect(doc).to create_autolinks + end - it 'parses label references' do - body = get_section('labelreferencefilter') - expect(body).to have_selector('a.gfm.gfm-label', count: 3) + it 'includes all reference filters' do + aggregate_failures do + expect(doc).to reference_users + expect(doc).to reference_issues + expect(doc).to reference_merge_requests + expect(doc).to reference_snippets + expect(doc).to reference_commit_ranges + expect(doc).to reference_commits + expect(doc).to reference_labels end end - describe 'Task Lists' do - it 'generates task lists' do - body = get_section('task-lists') - - aggregate_failures do - expect(body).to have_selector('ul.task-list', count: 2) - expect(body).to have_selector('li.task-list-item', count: 7) - expect(body).to have_selector('input[checked]', count: 3) - end - end + it 'includes TaskListFilter' do + expect(doc).to parse_task_lists end end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb new file mode 100644 index 00000000000..9df226c3af8 --- /dev/null +++ b/spec/support/matchers/markdown_matchers.rb @@ -0,0 +1,156 @@ +# MarkdownMatchers +# +# Custom matchers for our custom HTML::Pipeline filters. These are used to test +# that specific filters are or are not used by our defined pipelines. +# +# Must be included manually. +module MarkdownMatchers + extend RSpec::Matchers::DSL + include Capybara::Node::Matchers + + # RelativeLinkFilter + matcher :parse_relative_links do + set_default_markdown_messages + + match do |actual| + link = actual.at_css('a:contains("Relative Link")') + image = actual.at_css('img[alt="Relative Image"]') + + expect(link['href']).to end_with('master/doc/README.md') + expect(image['src']).to end_with('master/app/assets/images/touch-icon-ipad.png') + end + end + + # EmojiFilter + matcher :parse_emoji do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('img.emoji', count: 10) + end + end + + # TableOfContentsFilter + matcher :create_header_links do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('h1 a#gitlab-markdown') + expect(actual).to have_selector('h2 a#markdown') + expect(actual).to have_selector('h3 a#autolinkfilter') + end + end + + # AutolinkFilter + matcher :create_autolinks do + def have_autolink(link) + have_link(link, href: link) + end + + set_default_markdown_messages + + match do |actual| + expect(actual).to have_autolink('http://about.gitlab.com/') + expect(actual).to have_autolink('https://google.com/') + expect(actual).to have_autolink('ftp://ftp.us.debian.org/debian/') + expect(actual).to have_autolink('smb://foo/bar/baz') + expect(actual).to have_autolink('irc://irc.freenode.net/git') + expect(actual).to have_autolink('http://localhost:3000') + + %w(code a kbd).each do |elem| + expect(body).not_to have_selector("#{elem} a") + end + end + end + + # UserReferenceFilter + matcher :reference_users do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3) + end + end + + # IssueReferenceFilter + matcher :reference_issues do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-issue', count: 3) + end + end + + # MergeRequestReferenceFilter + matcher :reference_merge_requests do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3) + expect(actual).to have_selector('em a.gfm-merge_request') + end + end + + # SnippetReferenceFilter + matcher :reference_snippets do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2) + end + end + + # CommitRangeReferenceFilter + matcher :reference_commit_ranges do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2) + end + end + + # CommitReferenceFilter + matcher :reference_commits do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-commit', count: 2) + end + end + + # LabelReferenceFilter + matcher :reference_labels do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-label', count: 3) + end + end + + # TaskListFilter + matcher :parse_task_lists do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('ul.task-list', count: 2) + expect(actual).to have_selector('li.task-list-item', count: 7) + expect(actual).to have_selector('input[checked]', count: 3) + end + end +end + +# Monkeypatch the matcher DSL so that we can reduce some noisy duplication for +# setting the failure messages for these matchers +module RSpec::Matchers::DSL::Macros + def set_default_markdown_messages + failure_message do + # expected to parse emoji, but didn't + "expected to #{description}, but didn't" + end + + failure_message_when_negated do + # expected not to parse task lists, but did + "expected not to #{description}, but did" + end + end +end |