summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
blob: 7f4d67e403fac9c59ef3f6c4ab2b20a3c67d34eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# encoding: UTF-8

require 'spec_helper'

module Gitlab::Markdown
  describe RelativeLinkFilter do
    def filter(doc)
      described_class.call(doc, {
        commit:         project.commit,
        project:        project,
        project_wiki:   project_wiki,
        ref:            ref,
        requested_path: requested_path
      })
    end

    def image(path)
      %(<img src="#{path}" />)
    end

    def link(path)
      %(<a href="#{path}">#{path}</a>)
    end

    let(:project)        { create(:project) }
    let(:project_path)   { project.path_with_namespace }
    let(:ref)            { 'markdown' }
    let(:project_wiki)   { nil }
    let(:requested_path) { '/' }

    shared_examples :preserve_unchanged do
      it 'does not modify any relative URL in anchor' do
        doc = filter(link('README.md'))
        expect(doc.at_css('a')['href']).to eq 'README.md'
      end

      it 'does not modify any relative URL in image' do
        doc = filter(image('files/images/logo-black.png'))
        expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png'
      end
    end

    shared_examples :relative_to_requested do
      it 'rebuilds URL relative to the requested path' do
        doc = filter(link('users.md'))
        expect(doc.at_css('a')['href']).
          to eq "/#{project_path}/blob/#{ref}/doc/api/users.md"
      end
    end

    context 'with a project_wiki' do
      let(:project_wiki) { double('ProjectWiki') }
      include_examples :preserve_unchanged
    end

    context 'without a repository' do
      let(:project) { create(:empty_project) }
      include_examples :preserve_unchanged
    end

    context 'with an empty repository' do
      let(:project) { create(:project_empty_repo) }
      include_examples :preserve_unchanged
    end

    it 'does not raise an exception on invalid URIs' do
      act = link("://foo")
      expect { filter(act) }.not_to raise_error
    end

    context 'with a valid repository' do
      it 'rebuilds relative URL for a file in the repo' do
        doc = filter(link('doc/api/README.md'))
        expect(doc.at_css('a')['href']).
          to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
      end

      it 'rebuilds relative URL for a file in the repo with an anchor' do
        doc = filter(link('README.md#section'))
        expect(doc.at_css('a')['href']).
          to eq "/#{project_path}/blob/#{ref}/README.md#section"
      end

      it 'rebuilds relative URL for a directory in the repo' do
        doc = filter(link('doc/api/'))
        expect(doc.at_css('a')['href']).
          to eq "/#{project_path}/tree/#{ref}/doc/api"
      end

      it 'rebuilds relative URL for an image in the repo' do
        doc = filter(link('files/images/logo-black.png'))
        expect(doc.at_css('a')['href']).
          to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
      end

      it 'does not modify relative URL with an anchor only' do
        doc = filter(link('#section-1'))
        expect(doc.at_css('a')['href']).to eq '#section-1'
      end

      it 'does not modify absolute URL' do
        doc = filter(link('http://example.com'))
        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
      end

      context 'when requested path is a directory in the repo' do
        let(:requested_path) { 'doc/api' }
        include_examples :relative_to_requested
      end
    end
  end
end