mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-19 01:26:08 +10:00
Merge branch 'master' into project-buttons
This commit is contained in:
@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
||||
v 7.14.0 (unreleased)
|
||||
- Fix network graph when branch name has single quotes (Stan Hu)
|
||||
- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
|
||||
- Add support for Unicode filenames in relative links (Hiroyuki Sato)
|
||||
- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
|
||||
- Fix commit data retrieval when branch name has single quotes (Stan Hu)
|
||||
- Check that project was actually created rather than just validated in import:repos task (Stan Hu)
|
||||
@@ -17,9 +18,11 @@ v 7.14.0 (unreleased)
|
||||
- Expire Rails cache entries after two weeks to prevent endless Redis growth
|
||||
- Add support for destroying project milestones (Stan Hu)
|
||||
- Add fetch command to the MR page
|
||||
- Add project star and fork count, group avatar URL and user/group web URL attributes to API
|
||||
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
|
||||
- Add fetch command to the MR page.
|
||||
- Tweak project page buttons.
|
||||
- Disabled autocapitalize and autocorrect on login field (Daryl Chan)
|
||||
|
||||
v 7.13.2
|
||||
- Fix randomly failed spec
|
||||
@@ -30,7 +33,6 @@ v 7.13.2
|
||||
- Show the first tab automatically on MergeRequests#new
|
||||
- Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
|
||||
- Fix Gmail Actions
|
||||
- Disabled autocapitalize and autocorrect on login field (Daryl Chan)
|
||||
|
||||
v 7.13.1
|
||||
- Fix: Label modifications are not reflected in existing notes and in the issue list
|
||||
|
||||
@@ -56,6 +56,12 @@ class Group < Namespace
|
||||
name
|
||||
end
|
||||
|
||||
def avatar_url(size = nil)
|
||||
if avatar.present?
|
||||
[gitlab_config.url, avatar.url].join
|
||||
end
|
||||
end
|
||||
|
||||
def owners
|
||||
@owners ||= group_members.owners.map(&:user)
|
||||
end
|
||||
|
||||
@@ -36,7 +36,6 @@ class Project < ActiveRecord::Base
|
||||
include Gitlab::ConfigHelper
|
||||
include Gitlab::ShellAdapter
|
||||
include Gitlab::VisibilityLevel
|
||||
include Rails.application.routes.url_helpers
|
||||
include Referable
|
||||
include Sortable
|
||||
|
||||
@@ -316,7 +315,7 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def web_url
|
||||
[gitlab_config.url, path_with_namespace].join('/')
|
||||
Rails.application.routes.url_helpers.namespace_project_url(self.namespace, self)
|
||||
end
|
||||
|
||||
def web_url_without_protocol
|
||||
@@ -433,7 +432,7 @@ class Project < ActiveRecord::Base
|
||||
if avatar.present?
|
||||
[gitlab_config.url, avatar.url].join
|
||||
elsif avatar_in_git
|
||||
[gitlab_config.url, namespace_project_avatar_path(namespace, self)].join
|
||||
Rails.application.routes.url_helpers.namespace_project_avatar_url(namespace, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -571,7 +570,7 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def http_url_to_repo
|
||||
[gitlab_config.url, '/', path_with_namespace, '.git'].join('')
|
||||
"#{web_url}.git"
|
||||
end
|
||||
|
||||
# Check if current branch name is marked as protected in the system
|
||||
|
||||
@@ -6,6 +6,10 @@ module API
|
||||
|
||||
class UserBasic < UserSafe
|
||||
expose :id, :state, :avatar_url
|
||||
|
||||
expose :web_url do |user, options|
|
||||
Rails.application.routes.url_helpers.user_url(user)
|
||||
end
|
||||
end
|
||||
|
||||
class User < UserBasic
|
||||
@@ -59,6 +63,7 @@ module API
|
||||
expose :namespace
|
||||
expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
|
||||
expose :avatar_url
|
||||
expose :star_count, :forks_count
|
||||
end
|
||||
|
||||
class ProjectMember < UserBasic
|
||||
@@ -69,6 +74,11 @@ module API
|
||||
|
||||
class Group < Grape::Entity
|
||||
expose :id, :name, :path, :description
|
||||
expose :avatar_url
|
||||
|
||||
expose :web_url do |group, options|
|
||||
Rails.application.routes.url_helpers.group_url(group)
|
||||
end
|
||||
end
|
||||
|
||||
class GroupDetail < Group
|
||||
|
||||
@@ -7,7 +7,11 @@ module Backup
|
||||
def initialize
|
||||
@config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
|
||||
@db_dir = File.join(Gitlab.config.backup.path, 'db')
|
||||
FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir)
|
||||
FileUtils.rm_rf(@db_dir)
|
||||
# Ensure the parent dir of @db_dir exists
|
||||
FileUtils.mkdir_p(Gitlab.config.backup.path)
|
||||
# Fail if somebody raced to create @db_dir before us
|
||||
FileUtils.mkdir(@db_dir, mode: 0700)
|
||||
end
|
||||
|
||||
def dump
|
||||
@@ -25,7 +29,6 @@ module Backup
|
||||
abort 'Backup failed' unless success
|
||||
|
||||
$progress.print 'Compressing database ... '
|
||||
FileUtils.rm_f db_file_name_gz
|
||||
success = system('gzip', db_file_name)
|
||||
report_success(success)
|
||||
abort 'Backup failed: compress error' unless success
|
||||
|
||||
@@ -16,8 +16,6 @@ module Backup
|
||||
file << s.to_yaml.gsub(/^---\n/,'')
|
||||
end
|
||||
|
||||
FileUtils.chmod(0700, folders_to_backup)
|
||||
|
||||
# create archive
|
||||
$progress.print "Creating backup archive: #{tar_file} ... "
|
||||
orig_umask = File.umask(0077)
|
||||
|
||||
@@ -130,7 +130,10 @@ module Backup
|
||||
|
||||
def prepare
|
||||
FileUtils.rm_rf(backup_repos_path)
|
||||
FileUtils.mkdir_p(backup_repos_path)
|
||||
# Ensure the parent dir of backup_repos_path exists
|
||||
FileUtils.mkdir_p(Gitlab.config.backup.path)
|
||||
# Fail if somebody raced to create backup_repos_path before us
|
||||
FileUtils.mkdir(backup_repos_path, mode: 0700)
|
||||
end
|
||||
|
||||
def silent
|
||||
|
||||
@@ -10,7 +10,11 @@ module Backup
|
||||
|
||||
# Copy uploads from public/uploads to backup/uploads
|
||||
def dump
|
||||
FileUtils.mkdir_p(backup_uploads_dir)
|
||||
FileUtils.rm_rf(backup_uploads_dir)
|
||||
# Ensure the parent dir of backup_uploads_dir exists
|
||||
FileUtils.mkdir_p(Gitlab.config.backup.path)
|
||||
# Fail if somebody raced to create backup_uploads_dir before us
|
||||
FileUtils.mkdir(backup_uploads_dir, mode: 0700)
|
||||
FileUtils.cp_r(app_uploads_dir, backup_dir)
|
||||
end
|
||||
|
||||
|
||||
@@ -98,15 +98,25 @@ module Gitlab
|
||||
#
|
||||
# Returns a String
|
||||
def path_type(path)
|
||||
if repository.tree(current_sha, path).entries.any?
|
||||
unescaped_path = Addressable::URI.unescape(path)
|
||||
|
||||
if tree?(unescaped_path)
|
||||
'tree'
|
||||
elsif repository.blob_at(current_sha, path).try(:image?)
|
||||
elsif image?(unescaped_path)
|
||||
'raw'
|
||||
else
|
||||
'blob'
|
||||
end
|
||||
end
|
||||
|
||||
def tree?(path)
|
||||
repository.tree(current_sha, path).entries.any?
|
||||
end
|
||||
|
||||
def image?(path)
|
||||
repository.blob_at(current_sha, path).try(:image?)
|
||||
end
|
||||
|
||||
def current_sha
|
||||
context[:commit].try(:id) ||
|
||||
ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha
|
||||
|
||||
@@ -485,7 +485,8 @@ namespace :gitlab do
|
||||
|
||||
if project.empty_repo?
|
||||
puts "repository is empty".magenta
|
||||
elsif File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path)
|
||||
elsif File.directory?(project_hook_directory) && File.directory?(gitlab_shell_hooks_path) &&
|
||||
(File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path))
|
||||
puts 'ok'.green
|
||||
else
|
||||
puts "wrong or missing hooks".red
|
||||
@@ -754,7 +755,7 @@ namespace :gitlab do
|
||||
print "Ruby version >= #{required_version} ? ... "
|
||||
|
||||
if current_version.valid? && required_version <= current_version
|
||||
puts "yes (#{current_version})".green
|
||||
puts "yes (#{current_version})".green
|
||||
else
|
||||
puts "no".red
|
||||
try_fixing_it(
|
||||
@@ -772,7 +773,7 @@ namespace :gitlab do
|
||||
print "Git version >= #{required_version} ? ... "
|
||||
|
||||
if current_version.valid? && required_version <= current_version
|
||||
puts "yes (#{current_version})".green
|
||||
puts "yes (#{current_version})".green
|
||||
else
|
||||
puts "no".red
|
||||
try_fixing_it(
|
||||
@@ -806,4 +807,3 @@ namespace :gitlab do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+194
-389
@@ -17,26 +17,208 @@ 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
|
||||
#
|
||||
# See the MarkdownFeature class for setup details.
|
||||
|
||||
describe 'GitLab Markdown', feature: true do
|
||||
include ActionView::Helpers::TagHelper
|
||||
include ActionView::Helpers::UrlHelper
|
||||
include Capybara::Node::Matchers
|
||||
include GitlabMarkdownHelper
|
||||
include MarkdownMatchers
|
||||
|
||||
# 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
|
||||
|
||||
def doc(html = @html)
|
||||
Nokogiri::HTML::DocumentFragment.parse(html)
|
||||
end
|
||||
|
||||
# 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
|
||||
expect(doc.to_html).not_to match('foo<em>bar</em>baz')
|
||||
end
|
||||
|
||||
it 'parses table Markdown' do
|
||||
aggregate_failures do
|
||||
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
|
||||
|
||||
it 'allows Markdown in tables' do
|
||||
expect(doc.at_css('td:contains("Baz")').children.to_html).
|
||||
to eq '<strong>Baz</strong>'
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
it 'parses strikethroughs' do
|
||||
expect(doc).to have_selector(%{del:contains("and this text doesn't")})
|
||||
end
|
||||
|
||||
it 'parses superscript' do
|
||||
expect(doc).to have_selector('sup', count: 2)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'SanitizationFilter' do
|
||||
it 'permits b elements' do
|
||||
expect(doc).to have_selector('b:contains("b tag")')
|
||||
end
|
||||
|
||||
it 'permits em elements' do
|
||||
expect(doc).to have_selector('em:contains("em tag")')
|
||||
end
|
||||
|
||||
it 'permits code elements' do
|
||||
expect(doc).to have_selector('code:contains("code tag")')
|
||||
end
|
||||
|
||||
it 'permits kbd elements' do
|
||||
expect(doc).to have_selector('kbd:contains("s")')
|
||||
end
|
||||
|
||||
it 'permits strike elements' do
|
||||
expect(doc).to have_selector('strike:contains(Emoji)')
|
||||
end
|
||||
|
||||
it 'permits img elements' do
|
||||
expect(doc).to have_selector('img[src*="smile.png"]')
|
||||
end
|
||||
|
||||
it 'permits br elements' do
|
||||
expect(doc).to have_selector('br')
|
||||
end
|
||||
|
||||
it 'permits hr elements' do
|
||||
expect(doc).to have_selector('hr')
|
||||
end
|
||||
|
||||
it 'permits span elements' do
|
||||
expect(doc).to have_selector('span:contains("span tag")')
|
||||
end
|
||||
|
||||
it 'permits style attribute in th elements' do
|
||||
aggregate_failures do
|
||||
expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
|
||||
expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
|
||||
expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
|
||||
end
|
||||
end
|
||||
|
||||
it 'permits style attribute in td elements' do
|
||||
aggregate_failures do
|
||||
expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
|
||||
expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
|
||||
expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes `rel` attribute from links' do
|
||||
expect(doc).not_to have_selector('a[rel="bookmark"]')
|
||||
end
|
||||
|
||||
it "removes `href` from `a` elements if it's fishy" do
|
||||
expect(doc).not_to have_selector('a[href*="javascript"]')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Escaping' do
|
||||
it 'escapes non-tag angle brackets' do
|
||||
table = doc.css('table').last.at_css('tbody')
|
||||
expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Edge Cases' do
|
||||
it 'allows markup inside link elements' do
|
||||
aggregate_failures do
|
||||
expect(doc.at_css('a[href="#link-emphasis"]').to_html).
|
||||
to eq %{<a href="#link-emphasis"><em>text</em></a>}
|
||||
|
||||
expect(doc.at_css('a[href="#link-strong"]').to_html).
|
||||
to eq %{<a href="#link-strong"><strong>text</strong></a>}
|
||||
|
||||
expect(doc.at_css('a[href="#link-code"]').to_html).
|
||||
to eq %{<a href="#link-code"><code>text</code></a>}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ExternalLinkFilter' do
|
||||
it 'adds nofollow to external link' do
|
||||
link = doc.at_css('a:contains("Google")')
|
||||
expect(link.attr('rel')).to match 'nofollow'
|
||||
end
|
||||
|
||||
it 'ignores internal link' do
|
||||
link = doc.at_css('a:contains("GitLab Root")')
|
||||
expect(link.attr('rel')).not_to match 'nofollow'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'default pipeline' do
|
||||
before(:all) do
|
||||
@feat = MarkdownFeature.new
|
||||
|
||||
# `gfm_with_options` depends on a `@project` variable
|
||||
@project = @feat.project
|
||||
|
||||
@html = markdown(@feat.raw_markdown)
|
||||
end
|
||||
|
||||
it_behaves_like 'all pipelines'
|
||||
|
||||
it 'includes RelativeLinkFilter' do
|
||||
expect(doc).to parse_relative_links
|
||||
end
|
||||
|
||||
it 'includes EmojiFilter' do
|
||||
expect(doc).to parse_emoji
|
||||
end
|
||||
|
||||
it 'includes TableOfContentsFilter' do
|
||||
expect(doc).to create_header_links
|
||||
end
|
||||
|
||||
it 'includes AutolinkFilter' do
|
||||
expect(doc).to create_autolinks
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
it 'includes TaskListFilter' do
|
||||
expect(doc).to parse_task_lists
|
||||
end
|
||||
end
|
||||
|
||||
# `markdown` calls these two methods
|
||||
def current_user
|
||||
@@ -46,381 +228,4 @@ describe 'GitLab Markdown', feature: true do
|
||||
def user_color_scheme_class
|
||||
:white
|
||||
end
|
||||
|
||||
# Let's only parse this thing once
|
||||
before(:all) do
|
||||
@feat = MarkdownFeature.new
|
||||
|
||||
# `markdown` expects a `@project` variable
|
||||
@project = @feat.project
|
||||
|
||||
@md = markdown(@feat.raw_markdown)
|
||||
@doc = Nokogiri::HTML::DocumentFragment.parse(@md)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@feat.teardown
|
||||
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
|
||||
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 @md
|
||||
# end
|
||||
# end
|
||||
|
||||
describe 'Markdown' do
|
||||
describe 'No Intra Emphasis' 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')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Tables' do
|
||||
it 'parses table Markdown' do
|
||||
body = get_section('tables')
|
||||
expect(body).to have_selector('th:contains("Header")')
|
||||
expect(body).to have_selector('th:contains("Row")')
|
||||
expect(body).to have_selector('th:contains("Example")')
|
||||
end
|
||||
|
||||
it 'allows Markdown in tables' 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
|
||||
expect(@doc).to have_selector('pre.code.highlight.white.c')
|
||||
expect(@doc).to have_selector('pre.code.highlight.white.python')
|
||||
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')
|
||||
expect(body.to_html).to match('1<sup>st</sup>')
|
||||
expect(body.to_html).to match('2<sup>nd</sup>')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'HTML::Pipeline' do
|
||||
describe 'SanitizationFilter' do
|
||||
it 'uses a permissive whitelist' do
|
||||
expect(@doc).to have_selector('b:contains("b tag")')
|
||||
expect(@doc).to have_selector('em:contains("em tag")')
|
||||
expect(@doc).to have_selector('code:contains("code tag")')
|
||||
expect(@doc).to have_selector('kbd:contains("s")')
|
||||
expect(@doc).to have_selector('strike:contains(Emoji)')
|
||||
expect(@doc).to have_selector('img[src*="smile.png"]')
|
||||
expect(@doc).to have_selector('br')
|
||||
expect(@doc).to have_selector('hr')
|
||||
end
|
||||
|
||||
it 'permits span elements' do
|
||||
expect(@doc).to have_selector('span:contains("span tag")')
|
||||
end
|
||||
|
||||
it 'permits table alignment' do
|
||||
expect(@doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
|
||||
expect(@doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
|
||||
expect(@doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
|
||||
|
||||
expect(@doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
|
||||
expect(@doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
|
||||
expect(@doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
|
||||
end
|
||||
|
||||
it 'removes `rel` attribute from links' do
|
||||
body = get_section('sanitizationfilter')
|
||||
expect(body).not_to have_selector('a[rel="bookmark"]')
|
||||
end
|
||||
|
||||
it "removes `href` from `a` elements if it's fishy" do
|
||||
expect(@doc).not_to have_selector('a[href*="javascript"]')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Escaping' do
|
||||
let(:table) { @doc.css('table').last.at_css('tbody') }
|
||||
|
||||
it 'escapes non-tag angle brackets' do
|
||||
expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Edge Cases' do
|
||||
it 'allows markup inside link elements' do
|
||||
expect(@doc.at_css('a[href="#link-emphasis"]').to_html).
|
||||
to eq %{<a href="#link-emphasis"><em>text</em></a>}
|
||||
|
||||
expect(@doc.at_css('a[href="#link-strong"]').to_html).
|
||||
to eq %{<a href="#link-strong"><strong>text</strong></a>}
|
||||
|
||||
expect(@doc.at_css('a[href="#link-code"]').to_html).
|
||||
to eq %{<a href="#link-code"><code>text</code></a>}
|
||||
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
|
||||
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
|
||||
|
||||
describe 'AutolinkFilter' do
|
||||
let(:list) { get_section('autolinkfilter').next_element }
|
||||
|
||||
def item(index)
|
||||
list.at_css("li:nth-child(#{index})")
|
||||
end
|
||||
|
||||
it 'autolinks http://' do
|
||||
expect(item(1).children.first.name).to eq 'a'
|
||||
expect(item(1).children.first['href']).to eq 'http://about.gitlab.com/'
|
||||
end
|
||||
|
||||
it 'autolinks https://' do
|
||||
expect(item(2).children.first.name).to eq 'a'
|
||||
expect(item(2).children.first['href']).to eq 'https://google.com/'
|
||||
end
|
||||
|
||||
it 'autolinks ftp://' do
|
||||
expect(item(3).children.first.name).to eq 'a'
|
||||
expect(item(3).children.first['href']).to eq 'ftp://ftp.us.debian.org/debian/'
|
||||
end
|
||||
|
||||
it 'autolinks smb://' do
|
||||
expect(item(4).children.first.name).to eq 'a'
|
||||
expect(item(4).children.first['href']).to eq 'smb://foo/bar/baz'
|
||||
end
|
||||
|
||||
it 'autolinks irc://' do
|
||||
expect(item(5).children.first.name).to eq 'a'
|
||||
expect(item(5).children.first['href']).to eq 'irc://irc.freenode.net/git'
|
||||
end
|
||||
|
||||
it 'autolinks short, invalid URLs' do
|
||||
expect(item(6).children.first.name).to eq 'a'
|
||||
expect(item(6).children.first['href']).to eq 'http://localhost:3000'
|
||||
end
|
||||
|
||||
%w(code a kbd).each do |elem|
|
||||
it "ignores links inside '#{elem}' element" do
|
||||
body = get_section('autolinkfilter')
|
||||
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'
|
||||
end
|
||||
|
||||
it 'ignores internal link' do
|
||||
expect(links.css('a').last.to_html).not_to match 'nofollow'
|
||||
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
|
||||
|
||||
it 'parses issue references' do
|
||||
body = get_section('issuereferencefilter')
|
||||
expect(body).to have_selector('a.gfm.gfm-issue', count: 2)
|
||||
end
|
||||
|
||||
it 'parses merge request references' do
|
||||
body = get_section('mergerequestreferencefilter')
|
||||
expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2)
|
||||
end
|
||||
|
||||
it 'parses snippet references' do
|
||||
body = get_section('snippetreferencefilter')
|
||||
expect(body).to have_selector('a.gfm.gfm-snippet', count: 2)
|
||||
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 'parses commit references' do
|
||||
body = get_section('commitreferencefilter')
|
||||
expect(body).to have_selector('a.gfm.gfm-commit', count: 2)
|
||||
end
|
||||
|
||||
it 'parses label references' do
|
||||
body = get_section('labelreferencefilter')
|
||||
expect(body).to have_selector('a.gfm.gfm-label', count: 3)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Task Lists' do
|
||||
it 'generates task lists' do
|
||||
body = get_section('task-lists')
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
# This is a helper class used by the GitLab Markdown feature spec
|
||||
#
|
||||
# Because the feature spec only cares about the output of the Markdown, and the
|
||||
# test setup and teardown and parsing is fairly expensive, we only want to do it
|
||||
# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
|
||||
# block, so we fake it by encapsulating all the shared setup in this class.
|
||||
#
|
||||
# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for
|
||||
# reference to the factory-created objects.
|
||||
class MarkdownFeature
|
||||
include FactoryGirl::Syntax::Methods
|
||||
|
||||
def initialize
|
||||
DatabaseCleaner.start
|
||||
end
|
||||
|
||||
def teardown
|
||||
DatabaseCleaner.clean
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= create(:user)
|
||||
end
|
||||
|
||||
def group
|
||||
unless @group
|
||||
@group = create(:group)
|
||||
@group.add_user(user, Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
|
||||
@group
|
||||
end
|
||||
|
||||
# Direct references ----------------------------------------------------------
|
||||
|
||||
def project
|
||||
@project ||= create(:project)
|
||||
end
|
||||
|
||||
def issue
|
||||
@issue ||= create(:issue, project: project)
|
||||
end
|
||||
|
||||
def merge_request
|
||||
@merge_request ||= create(:merge_request, :simple, source_project: project)
|
||||
end
|
||||
|
||||
def snippet
|
||||
@snippet ||= create(:project_snippet, project: project)
|
||||
end
|
||||
|
||||
def commit
|
||||
@commit ||= project.commit
|
||||
end
|
||||
|
||||
def commit_range
|
||||
unless @commit_range
|
||||
commit2 = project.commit('HEAD~3')
|
||||
@commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
|
||||
end
|
||||
|
||||
@commit_range
|
||||
end
|
||||
|
||||
def simple_label
|
||||
@simple_label ||= create(:label, name: 'gfm', project: project)
|
||||
end
|
||||
|
||||
def label
|
||||
@label ||= create(:label, name: 'awaiting feedback', project: project)
|
||||
end
|
||||
|
||||
# Cross-references -----------------------------------------------------------
|
||||
|
||||
def xproject
|
||||
unless @xproject
|
||||
namespace = create(:namespace, name: 'cross-reference')
|
||||
@xproject = create(:project, namespace: namespace)
|
||||
@xproject.team << [user, :developer]
|
||||
end
|
||||
|
||||
@xproject
|
||||
end
|
||||
|
||||
def xissue
|
||||
@xissue ||= create(:issue, project: xproject)
|
||||
end
|
||||
|
||||
def xmerge_request
|
||||
@xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
|
||||
end
|
||||
|
||||
def xsnippet
|
||||
@xsnippet ||= create(:project_snippet, project: xproject)
|
||||
end
|
||||
|
||||
def xcommit
|
||||
@xcommit ||= xproject.commit
|
||||
end
|
||||
|
||||
def xcommit_range
|
||||
unless @xcommit_range
|
||||
xcommit2 = xproject.commit('HEAD~2')
|
||||
@xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
|
||||
end
|
||||
|
||||
@xcommit_range
|
||||
end
|
||||
|
||||
def raw_markdown
|
||||
fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
|
||||
ERB.new(File.read(fixture)).result(binding)
|
||||
end
|
||||
end
|
||||
|
||||
Vendored
+10
-3
@@ -100,6 +100,13 @@ Markdown should be usable inside a link. Let's try!
|
||||
- [**text**](#link-strong)
|
||||
- [`text`](#link-code)
|
||||
|
||||
### RelativeLinkFilter
|
||||
|
||||
Linking to a file relative to this project's repository should work.
|
||||
|
||||
[Relative Link](doc/README.md)
|
||||

|
||||
|
||||
### EmojiFilter
|
||||
|
||||
Because life would be :zzz: without Emoji, right? :rocket:
|
||||
@@ -123,9 +130,9 @@ These are all plain text that should get turned into links:
|
||||
|
||||
But it shouldn't autolink text inside certain tags:
|
||||
|
||||
- <code>http://about.gitlab.com/</code>
|
||||
- <a>http://about.gitlab.com/</a>
|
||||
- <kbd>http://about.gitlab.com/</kbd>
|
||||
- <code>http://code.gitlab.com/</code>
|
||||
- <a>http://a.gitlab.com/</a>
|
||||
- <kbd>http://kbd.gitlab.com/</kbd>
|
||||
|
||||
### ExternalLinkFilter
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Gitlab::Markdown
|
||||
@@ -101,6 +103,20 @@ module Gitlab::Markdown
|
||||
expect(doc.at_css('a')['href']).to eq 'http://example.com'
|
||||
end
|
||||
|
||||
it 'supports Unicode filenames' do
|
||||
path = 'files/images/한글.png'
|
||||
escaped = Addressable::URI.escape(path)
|
||||
|
||||
# Stub these methods so the file doesn't actually need to be in the repo
|
||||
allow_any_instance_of(described_class).to receive(:file_exists?).
|
||||
and_return(true)
|
||||
allow_any_instance_of(described_class).
|
||||
to receive(:image?).with(path).and_return(true)
|
||||
|
||||
doc = filter(image(escaped))
|
||||
expect(doc.at_css('img')['src']).to match '/raw/'
|
||||
end
|
||||
|
||||
context 'when requested path is a file in the repo' do
|
||||
let(:requested_path) { 'doc/api/README.md' }
|
||||
include_examples :relative_to_requested
|
||||
|
||||
@@ -111,14 +111,20 @@ describe Project do
|
||||
expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
|
||||
end
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
project = Project.new(path: 'somewhere')
|
||||
expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere")
|
||||
describe "#web_url" do
|
||||
let(:project) { create(:empty_project, path: "somewhere") }
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.path}/somewhere")
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the web URL without the protocol for this repo' do
|
||||
project = Project.new(path: 'somewhere')
|
||||
expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere")
|
||||
describe "#web_url_without_protocol" do
|
||||
let(:project) { create(:empty_project, path: "somewhere") }
|
||||
|
||||
it 'returns the web URL without the protocol for this repo' do
|
||||
expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/#{project.namespace.path}/somewhere")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'last_activity methods' do
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# This is a helper class used by the GitLab Markdown feature spec
|
||||
#
|
||||
# Because the feature spec only cares about the output of the Markdown, and the
|
||||
# test setup and teardown and parsing is fairly expensive, we only want to do it
|
||||
# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
|
||||
# block, so we fake it by encapsulating all the shared setup in this class.
|
||||
#
|
||||
# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for
|
||||
# reference to the factory-created objects.
|
||||
class MarkdownFeature
|
||||
include FactoryGirl::Syntax::Methods
|
||||
|
||||
def user
|
||||
@user ||= create(:user)
|
||||
end
|
||||
|
||||
def group
|
||||
unless @group
|
||||
@group = create(:group)
|
||||
@group.add_user(user, Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
|
||||
@group
|
||||
end
|
||||
|
||||
# Direct references ----------------------------------------------------------
|
||||
|
||||
def project
|
||||
@project ||= create(:project)
|
||||
end
|
||||
|
||||
def issue
|
||||
@issue ||= create(:issue, project: project)
|
||||
end
|
||||
|
||||
def merge_request
|
||||
@merge_request ||= create(:merge_request, :simple, source_project: project)
|
||||
end
|
||||
|
||||
def snippet
|
||||
@snippet ||= create(:project_snippet, project: project)
|
||||
end
|
||||
|
||||
def commit
|
||||
@commit ||= project.commit
|
||||
end
|
||||
|
||||
def commit_range
|
||||
unless @commit_range
|
||||
commit2 = project.commit('HEAD~3')
|
||||
@commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
|
||||
end
|
||||
|
||||
@commit_range
|
||||
end
|
||||
|
||||
def simple_label
|
||||
@simple_label ||= create(:label, name: 'gfm', project: project)
|
||||
end
|
||||
|
||||
def label
|
||||
@label ||= create(:label, name: 'awaiting feedback', project: project)
|
||||
end
|
||||
|
||||
# Cross-references -----------------------------------------------------------
|
||||
|
||||
def xproject
|
||||
unless @xproject
|
||||
namespace = create(:namespace, name: 'cross-reference')
|
||||
@xproject = create(:project, namespace: namespace)
|
||||
@xproject.team << [user, :developer]
|
||||
end
|
||||
|
||||
@xproject
|
||||
end
|
||||
|
||||
def xissue
|
||||
@xissue ||= create(:issue, project: xproject)
|
||||
end
|
||||
|
||||
def xmerge_request
|
||||
@xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
|
||||
end
|
||||
|
||||
def xsnippet
|
||||
@xsnippet ||= create(:project_snippet, project: xproject)
|
||||
end
|
||||
|
||||
def xcommit
|
||||
@xcommit ||= xproject.commit
|
||||
end
|
||||
|
||||
def xcommit_range
|
||||
unless @xcommit_range
|
||||
xcommit2 = xproject.commit('HEAD~2')
|
||||
@xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
|
||||
end
|
||||
|
||||
@xcommit_range
|
||||
end
|
||||
|
||||
def raw_markdown
|
||||
fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
|
||||
ERB.new(File.read(fixture)).result(binding)
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user