File: respond_with_spec.rb

package info (click to toggle)
ruby-sinatra 4.2.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,944 kB
  • sloc: ruby: 17,702; sh: 25; makefile: 8
file content (311 lines) | stat: -rw-r--r-- 8,668 bytes parent folder | download | duplicates (3)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
require 'multi_json'

require 'spec_helper'
require 'okjson'

RSpec.describe Sinatra::RespondWith do
  def respond_app(&block)
    mock_app do
      set :app_file, __FILE__
      set :views, root + '/respond_with'
      register Sinatra::RespondWith
      class_eval(&block)
    end
  end

  def respond_to(*args, &block)
    respond_app { get('/') { respond_to(*args, &block) } }
  end

  def respond_with(*args, &block)
    respond_app { get('/') { respond_with(*args, &block) } }
  end

  def req(*types)
    path = types.shift if types.first.is_a?(String) && types.first.start_with?('/')
    accept = types.map { |t| Sinatra::Base.mime_type(t).to_s }.join ','
    get (path || '/'), {}, 'HTTP_ACCEPT' => accept
  end

  describe "Helpers#respond_to" do
    it 'allows defining handlers by file extensions' do
      respond_to do |format|
        format.html { "html!" }
        format.json { "json!" }
      end

      expect(req(:html).body).to eq("html!")
      expect(req(:json).body).to eq("json!")
    end

    it 'respects quality' do
      respond_to do |format|
        format.html { "html!" }
        format.json { "json!" }
      end

      expect(req("text/html;q=0.7, application/json;q=0.3").body).to eq("html!")
      expect(req("text/html;q=0.3, application/json;q=0.7").body).to eq("json!")
    end

    it 'allows using mime types' do
      respond_to do |format|
        format.on('text/html') { "html!" }
        format.json { "json!" }
      end

      expect(req(:html).body).to eq("html!")
    end

    it 'allows using wildcards in format matchers' do
      respond_to do |format|
        format.on('text/*') { "text!" }
        format.json { "json!" }
      end

      expect(req(:html).body).to eq("text!")
    end

    it 'allows using catch all wildcards in format matchers' do
      respond_to do |format|
        format.on('*/*') { "anything!" }
        format.json { "json!" }
      end

      expect(req(:html).body).to eq("anything!")
    end

    it 'prefers concret over generic' do
      respond_to do |format|
        format.on('text/*') { "text!" }
        format.on('*/*') { "anything!" }
        format.json { "json!" }
      end

      expect(req(:json).body).to eq("json!")
      expect(req(:html).body).to eq("text!")
    end

    it 'does not set up default handlers' do
      respond_to
      expect(req).not_to be_ok
      expect(status).to eq(500)
      expect(body).to eq("Unknown template engine")
    end
  end

  describe "Helpers#respond_with" do
    describe "matching" do
      it 'allows defining handlers by file extensions' do
        respond_with(:ignore) do |format|
          format.html { "html!" }
          format.json { "json!" }
        end

        expect(req(:html).body).to eq("html!")
        expect(req(:json).body).to eq("json!")
      end

      it 'respects quality' do
        respond_with(:ignore) do |format|
          format.html { "html!" }
          format.json { "json!" }
        end

        expect(req("text/html;q=0.7, application/json;q=0.3").body).to eq("html!")
        expect(req("text/html;q=0.3, application/json;q=0.7").body).to eq("json!")
      end

      it 'allows using mime types' do
        respond_with(:ignore) do |format|
          format.on('text/html') { "html!" }
          format.json { "json!" }
        end

        expect(req(:html).body).to eq("html!")
      end

      it 'allows using wildcards in format matchers' do
        respond_with(:ignore) do |format|
          format.on('text/*') { "text!" }
          format.json { "json!" }
        end

        expect(req(:html).body).to eq("text!")
      end

      it 'allows using catch all wildcards in format matchers' do
        respond_with(:ignore) do |format|
          format.on('*/*') { "anything!" }
          format.json { "json!" }
        end

        expect(req(:html).body).to eq("anything!")
      end

      it 'prefers concret over generic' do
        respond_with(:ignore) do |format|
          format.on('text/*') { "text!" }
          format.on('*/*') { "anything!" }
          format.json { "json!" }
        end

        expect(req(:json).body).to eq("json!")
        expect(req(:html).body).to eq("text!")
      end
    end

    describe "default behavior" do
      it 'converts objects to json out of the box' do
        respond_with 'a' => 'b'
        expect(OkJson.decode(req(:json).body)).to eq({'a' => 'b'})
      end

      it 'handles multiple routes correctly' do
        respond_app do
          get('/') { respond_with 'a' => 'b' }
          get('/:name') { respond_with 'a' => params[:name] }
        end
        expect(OkJson.decode(req('/',  :json).body)).to eq({'a' => 'b'})
        expect(OkJson.decode(req('/b', :json).body)).to eq({'a' => 'b'})
        expect(OkJson.decode(req('/c', :json).body)).to eq({'a' => 'c'})
      end

      it "calls to_EXT if available" do
        respond_with Struct.new(:to_pdf).new("hello")
        expect(req(:pdf).body).to eq("hello")
      end

      it 'results in a 500 if format cannot be produced' do
        respond_with({})
        expect(req(:html)).not_to be_ok
        expect(status).to eq(500)
        expect(body).to eq("Unknown template engine")
      end
    end

    describe 'templates' do
      it 'looks for templates with name.target.engine' do
        respond_with :foo, :name => 'World'
        expect(req(:html)).to be_ok
        expect(body).to eq("Hello World!")
      end

      it 'looks for templates with name.engine for specific engines' do
        respond_with :bar
        expect(req(:html)).to be_ok
        expect(body).to eq("guten Tag!")
      end

      it 'does not use name.engine for engines producing other formats' do
        respond_with :not_html
        expect(req(:html)).not_to be_ok
        expect(status).to eq(500)
        expect(body).to eq("Unknown template engine")
      end

      it 'falls back to #json if no template is found' do
        respond_with :foo, :name => 'World'
        expect(req(:json)).to be_ok
        expect(OkJson.decode(body)).to eq({'name' => 'World'})
      end

      it 'favors templates over #json' do
        respond_with :bar, :name => 'World'
        expect(req(:json)).to be_ok
        expect(body).to eq('json!')
      end

      it 'falls back to to_EXT if no template is found' do
        object = {:name => 'World'}
        def object.to_pdf; "hi" end
        respond_with :foo, object
        expect(req(:pdf)).to be_ok
        expect(body).to eq("hi")
      end

      unless defined? JRUBY_VERSION
        it 'uses yajl for json' do
          respond_with :baz
          expect(req(:json)).to be_ok
          expect(body).to eq("\"yajl!\"")
        end
      end
    end

    describe 'customizing' do
      it 'allows customizing' do
        respond_with(:foo, :name => 'World') { |f| f.html { 'html!' }}
        expect(req(:html)).to be_ok
        expect(body).to eq("html!")
      end

      it 'falls back to default behavior if none matches' do
        respond_with(:foo, :name => 'World') { |f| f.json { 'json!' }}
        expect(req(:html)).to be_ok
        expect(body).to eq("Hello World!")
      end

      it 'favors generic rule over default behavior' do
        respond_with(:foo, :name => 'World') { |f| f.on('*/*') { 'generic!' }}
        expect(req(:html)).to be_ok
        expect(body).to eq("generic!")
      end
    end

    describe "inherited" do
      it "registers RespondWith in an inherited app" do
        app = Sinatra.new do
          set :app_file, __FILE__
          set :views, root + '/respond_with'
          register Sinatra::RespondWith

          get '/a' do
            respond_with :json
          end
        end

        self.app = Sinatra.new(app)
        expect(req('/a', :json)).not_to be_ok
      end
    end
  end

  describe :respond_to do
    it 'acts as global provides condition' do
      respond_app do
        respond_to :json, :html
        get('/a') { 'ok' }
        get('/b') { 'ok' }
      end

      expect(req('/b', :xml)).not_to be_ok
      expect(req('/b', :html)).to be_ok
    end

    it 'still allows provides' do
      respond_app do
        respond_to :json, :html
        get('/a') { 'ok' }
        get('/b', :provides => :json) { 'ok' }
      end

      expect(req('/b', :html)).not_to be_ok
      expect(req('/b', :json)).to be_ok
    end

    it 'plays well with namespaces' do
      respond_app do
        register Sinatra::Namespace
        namespace '/a' do
          respond_to :json
          get { 'json' }
        end
        get('/b') { 'anything' }
      end

      expect(req('/a', :html)).not_to be_ok
      expect(req('/b', :html)).to be_ok
    end
  end
end