imgix 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class DomainsTest < Imgix::Test
6
+ def test_invalid_domain_append_slash
7
+ assert_raises(ArgumentError) {Imgix::Client.new(host: "assets.imgix.net/")}
8
+ end
9
+
10
+ def test_invalid_domain_prepend_scheme
11
+ assert_raises(ArgumentError) {Imgix::Client.new(host: "https://assets.imgix.net")}
12
+ end
13
+
14
+ def test_invalid_domain_append_dash
15
+ assert_raises(ArgumentError) {Imgix::Client.new(host: "assets.imgix.net-")}
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ParamHelpers < Imgix::Test
6
+ def test_param_helpers_emits_dep_warning
7
+ host_warn = "Warning: The identifier `host' has been deprecated and " \
8
+ "will\nappear as `domain' in the next major version, e.g. " \
9
+ "`@host'\nbecomes `@domain', `options[:host]' becomes " \
10
+ "`options[:domain]'.\n"
11
+
12
+ assert_output(nil, host_warn) {
13
+ client = Imgix::Client.new(host: 'test.imgix.net')
14
+
15
+ rect_warn = "Warning: `ParamHelpers.rect` has been deprecated and " \
16
+ "will be removed in the next major version.\n"
17
+
18
+ assert_output(nil, rect_warn){
19
+ client.path('/images/demo.png').rect(x: 0, y: 50, width: 200, height: 300)
20
+ }
21
+ }
22
+ end
23
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class PathTest < Imgix::Test
6
+ def test_prefix_with_arg_warns
7
+ prefix_warn = "Warning: `Client::prefix' will take zero arguments " \
8
+ "in the next major version.\n"
9
+
10
+ assert_output(nil, prefix_warn) {
11
+ Imgix::Client.new(
12
+ domain: 'test.imgix.net',
13
+ include_library_param: false
14
+ ).prefix("")
15
+ }
16
+
17
+ # `new_prefix' is a placeholder until the bump, when it will become
18
+ # `prefix`.
19
+ assert_output(nil, nil) {
20
+ Imgix::Client.new(
21
+ domain: 'test.imgix.net',
22
+ include_library_param: false
23
+ ).new_prefix
24
+ }
25
+ end
26
+
27
+ def test_creating_a_path
28
+ path = client.path('/images/demo.png')
29
+ assert_equal 'https://demo.imgix.net/images/demo.png?s=2c7c157eaf23b06a0deb2f60b81938c4', path.to_url
30
+
31
+ path = client.path('images/demo.png')
32
+ assert_equal 'https://demo.imgix.net/images/demo.png?s=2c7c157eaf23b06a0deb2f60b81938c4', path.to_url
33
+ end
34
+
35
+ def test_signing_path_with_param
36
+ url = 'https://demo.imgix.net/images/demo.png?w=200&s=da421114ca238d1f4a927b889f67c34e'
37
+ path = client.path('/images/demo.png')
38
+
39
+ assert_output(nil, "Warning: `Path.width=' has been deprecated and " \
40
+ "will be removed in the next major version (along " \
41
+ "with all parameter `ALIASES`).\n") {
42
+ path.width = 200
43
+ }
44
+
45
+ assert_equal url, path.to_url
46
+
47
+ path = client.path('/images/demo.png')
48
+ assert_equal url, path.to_url(w: 200)
49
+
50
+ path = client.path('/images/demo.png')
51
+
52
+ assert_output(nil, "Warning: `Path.width' has been deprecated and " \
53
+ "will be removed in the next major version (along " \
54
+ "with all parameter `ALIASES`).\n") {
55
+ assert_equal url, path.width(200).to_url
56
+ }
57
+ end
58
+
59
+ def test_resetting_defaults
60
+ url = 'https://demo.imgix.net/images/demo.png?w=200&s=da421114ca238d1f4a927b889f67c34e'
61
+ path = client.path('/images/demo.png')
62
+
63
+ assert_output(nil, "Warning: `Path.height=' has been deprecated and " \
64
+ "will be removed in the next major version (along " \
65
+ "with all parameter `ALIASES`).\n") {
66
+ path.height = 300
67
+ }
68
+
69
+ assert_equal url, path.defaults.to_url(w: 200)
70
+ end
71
+
72
+ def test_path_with_multiple_params
73
+ url = 'https://demo.imgix.net/images/demo.png?h=200&w=200&s=d570a1ecd765470f7b34a69b56718a7a'
74
+ path = client.path('/images/demo.png')
75
+
76
+ assert_equal url, path.to_url(h: 200, w: 200)
77
+
78
+ path = client.path('/images/demo.png')
79
+
80
+ assert_output(nil, "Warning: `Path.height' has been deprecated and " \
81
+ "will be removed in the next major version (along " \
82
+ "with all parameter `ALIASES`).\n") {
83
+ path.height(200)
84
+ }
85
+
86
+
87
+ assert_output(nil, "Warning: `Path.width' has been deprecated and " \
88
+ "will be removed in the next major version (along " \
89
+ "with all parameter `ALIASES`).\n") {
90
+ path.width(200)
91
+ }
92
+
93
+ assert_equal url, path.to_url
94
+ end
95
+
96
+ def test_path_with_multi_value_param_safely_encoded
97
+ url = 'https://demo.imgix.net/images/demo.png?markalign=middle%2Ccenter&s=f0d0e28a739f022638f4ba6dddf9b694'
98
+ path = client.path('/images/demo.png')
99
+
100
+ assert_equal url, path.markalign('middle', 'center').to_url
101
+ end
102
+
103
+ def test_param_keys_are_escaped
104
+ ix_url = unsigned_client.path('demo.png').to_url({
105
+ :'hello world' => 'interesting'
106
+ })
107
+
108
+ assert_equal "https://demo.imgix.net/demo.png?hello%20world=interesting", ix_url
109
+ end
110
+
111
+ def test_param_values_are_escaped
112
+ ix_url = unsigned_client.path('demo.png').to_url({
113
+ hello_world: '/foo"> <script>alert("hacked")</script><'
114
+ })
115
+
116
+ assert_equal "https://demo.imgix.net/demo.png?hello_world=%2Ffoo%22%3E%20%3Cscript%3Ealert%28%22hacked%22%29%3C%2Fscript%3E%3C", ix_url
117
+ end
118
+
119
+ def test_base64_param_variants_are_base64_encoded
120
+ ix_url = unsigned_client.path('~text').to_url({
121
+ txt64: 'I cannøt belîév∑ it wors! 😱'
122
+ })
123
+
124
+ assert_equal "https://demo.imgix.net/~text?txt64=SSBjYW5uw7h0IGJlbMOuw6l24oiRIGl0IHdvcu-jv3MhIPCfmLE", ix_url
125
+ end
126
+
127
+ def test_host_is_required
128
+ assert_raises(ArgumentError) {Imgix::Client.new}
129
+ end
130
+
131
+ def test_token_is_optional
132
+ client = Imgix::Client.new(host: 'demo.imgix.net', include_library_param: false)
133
+ url = 'https://demo.imgix.net/images/demo.png'
134
+ path = client.path('/images/demo.png')
135
+
136
+ assert_equal url, path.to_url
137
+ end
138
+
139
+ def test_https_is_optional
140
+ client = Imgix::Client.new(host: 'demo.imgix.net', include_library_param: false, use_https: false)
141
+ url = 'http://demo.imgix.net/images/demo.png'
142
+ path = client.path('/images/demo.png')
143
+
144
+ assert_equal url, path.to_url
145
+ end
146
+
147
+ def test_full_url
148
+ path = 'https://google.com/cats.gif'
149
+
150
+ assert_equal "https://demo.imgix.net/#{CGI.escape(path)}?s=e686099fbba86fc2b8141d3c1ff60605", client.path(path).to_url
151
+ end
152
+
153
+ def test_full_url_with_a_space
154
+ path = 'https://my-demo-site.com/files/133467012/avatar icon.png'
155
+ assert_equal "https://demo.imgix.net/#{CGI.escape(path)}?s=35ca40e2e7b6bd208be2c4f7073f658e", client.path(path).to_url
156
+ end
157
+
158
+ def test_include_library_param
159
+ client = Imgix::Client.new(host: 'demo.imgix.net') # enabled by default
160
+ url = client.path('/images/demo.png').to_url
161
+
162
+ assert_equal "ixlib=rb-#{Imgix::VERSION}", URI(url).query
163
+ end
164
+
165
+ def test_configure_library_param
166
+ library = "sinatra"
167
+ version = Imgix::VERSION
168
+ client = Imgix::Client.new(host: 'demo.imgix.net', library_param: library, library_version: version) # enabled by default
169
+ url = client.path('/images/demo.png').to_url
170
+
171
+ assert_equal "ixlib=#{library}-#{version}", URI(url).query
172
+ end
173
+
174
+ private
175
+ def client
176
+ @client ||= Imgix::Client.new(host: 'demo.imgix.net', secure_url_token: '10adc394', include_library_param: false)
177
+ end
178
+
179
+ def unsigned_client
180
+ @unsigned_client ||= Imgix::Client.new(host: 'demo.imgix.net', include_library_param: false)
181
+ end
182
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class PurgeTest < Imgix::Test
4
+ def test_runtime_error_without_api_key
5
+ assert_raises(RuntimeError) {
6
+ Imgix::Client.new(host: 'demo.imgix.net', include_library_param: false)
7
+ .purge('https://demo.imgix.net/images/demo.png')
8
+ }
9
+ end
10
+
11
+ def test_successful_purge
12
+ stub_request(:post, "https://api.imgix.com/v2/image/purger").
13
+ with(
14
+ body: {"url"=>"https://demo.imgix.net/images/demo.png"}).
15
+ to_return(status: 200)
16
+
17
+ Imgix::Client.new(host: 'demo.imgix.net', api_key: '10adc394')
18
+ .purge('/images/demo.png')
19
+
20
+ assert_requested :post, 'https://api.imgix.com/v2/image/purger',
21
+ body: 'url=https%3A%2F%2Fdemo.imgix.net%2Fimages%2Fdemo.png',
22
+ headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic MTBhZGMzOTQ6', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>"imgix rb-#{ Imgix::VERSION}"},
23
+ times: 1
24
+ end
25
+ end
@@ -0,0 +1,755 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module SrcsetTest
6
+ RESOLUTIONS = [
7
+ 100, 116, 135, 156, 181, 210, 244, 283,
8
+ 328, 380, 441, 512, 594, 689, 799, 927,
9
+ 1075, 1247, 1446, 1678, 1946, 2257, 2619,
10
+ 3038, 3524, 4087, 4741, 5500, 6380, 7401, 8192
11
+ ].freeze
12
+
13
+ DPR_QUALITY = [75, 50, 35, 23, 20].freeze
14
+
15
+ DOMAIN = 'testing.imgix.net'
16
+ TOKEN = 'MYT0KEN'
17
+ JPG_PATH = 'image.jpg'
18
+
19
+ def mock_client
20
+ Imgix::Client.new(
21
+ host: DOMAIN,
22
+ include_library_param: false
23
+ ).path(JPG_PATH)
24
+ end
25
+
26
+ def mock_signed_client
27
+ Imgix::Client.new(
28
+ host: DOMAIN,
29
+ secure_url_token: TOKEN,
30
+ include_library_param: false
31
+ ).path(JPG_PATH)
32
+ end
33
+
34
+ def signature_base(params)
35
+ TOKEN + '/' + JPG_PATH + params
36
+ end
37
+
38
+ def get_sig_from(src)
39
+ src.slice(src.index('s=') + 2, src.length)
40
+ end
41
+
42
+ def get_params_from(src)
43
+ src[src.index('?')..src.index('s=') - 2]
44
+ end
45
+
46
+ def get_expected_signature(src)
47
+ # Ensure signature param exists.
48
+ assert_includes src, 's='
49
+
50
+ params = get_params_from(src)
51
+ signature_base = signature_base(params)
52
+
53
+ Digest::MD5.hexdigest(signature_base)
54
+ end
55
+
56
+ class SrcsetDefault < Imgix::Test
57
+ include SrcsetTest
58
+
59
+ def test_no_parameters
60
+ srcset = path.to_srcset
61
+
62
+ expected_number_of_pairs = 31
63
+ assert_equal expected_number_of_pairs, srcset.split(',').length
64
+ end
65
+
66
+ def test_srcset_pair_values
67
+ resolutions = RESOLUTIONS
68
+ srcset = path.to_srcset
69
+ srclist = srcset.split(',').map do |srcset_split|
70
+ srcset_split.split(' ')[1].to_i
71
+ end
72
+
73
+ for i in 0..srclist.length - 1
74
+ assert_equal(srclist[i], resolutions[i])
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def path
81
+ @client ||= mock_signed_client
82
+ end
83
+ end
84
+
85
+ class SrcsetGivenWidth < Imgix::Test
86
+ include SrcsetTest
87
+
88
+ def test_srcset_in_dpr_form
89
+ device_pixel_ratio = 1
90
+
91
+ srcset.split(',').map do |src|
92
+ ratio = src.split(' ')[1]
93
+ assert_equal "#{device_pixel_ratio}x", ratio
94
+ device_pixel_ratio += 1
95
+ end
96
+ end
97
+
98
+ def test_srcset_has_dpr_params
99
+ i = 1
100
+ srcset.split(',').map do |srcset_split|
101
+ src = srcset_split.split(' ')[0]
102
+ assert_includes src, "dpr=#{i}"
103
+ i += 1
104
+ end
105
+ end
106
+
107
+ def test_srcset_signs_urls
108
+ srcset.split(',').map do |srcset_split|
109
+ src = srcset_split.split(' ')[0]
110
+ expected_signature = get_expected_signature(src)
111
+
112
+ assert_includes src, expected_signature
113
+ end
114
+ end
115
+
116
+ def test_srcset_has_variable_qualities
117
+ i = 0
118
+ srcset.split(',').map do |src|
119
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
120
+ i += 1
121
+ end
122
+ end
123
+
124
+ def test_srcset_respects_overriding_quality
125
+ quality_override = 100
126
+ srcset = mock_signed_client.to_srcset(w: 100, q: quality_override)
127
+
128
+ srcset.split(',').map do |src|
129
+ assert_includes src, "q=#{quality_override}"
130
+ end
131
+ end
132
+
133
+ def test_disable_variable_quality
134
+ srcset = mock_signed_client.to_srcset(
135
+ w: 100,
136
+ options: { disable_variable_quality: true }
137
+ )
138
+
139
+ srcset.split(',').map do |src|
140
+ assert(not(src.include?('q=')))
141
+ end
142
+ end
143
+
144
+ def test_respects_quality_param_when_disabled
145
+ quality_override = 100
146
+ srcset = mock_signed_client.to_srcset(
147
+ w: 100, q: 100,
148
+ options: { disable_variable_quality: true }
149
+ )
150
+
151
+ srcset.split(',').map do |src|
152
+ assert_includes src, "q=#{quality_override}"
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def srcset
159
+ @client ||= mock_signed_client.to_srcset(w: 100)
160
+ end
161
+ end
162
+
163
+ class SrcsetGivenHeight < Imgix::Test
164
+ include SrcsetTest
165
+
166
+ def test_srcset_generates_width_pairs
167
+ expected_number_of_pairs = 31
168
+ assert_equal expected_number_of_pairs, srcset.split(',').length
169
+ end
170
+
171
+ def test_srcset_pair_values
172
+ resolutions = RESOLUTIONS
173
+ srclist = srcset.split(',').map do |srcset_split|
174
+ srcset_split.split(' ')[1].to_i
175
+ end
176
+
177
+ for i in 0..srclist.length - 1
178
+ assert_equal(srclist[i], resolutions[i])
179
+ end
180
+ end
181
+
182
+ def test_srcset_respects_height_parameter
183
+ srcset.split(',').map do |src|
184
+ assert_includes src, 'h='
185
+ end
186
+ end
187
+
188
+ def test_srcset_within_bounds
189
+ min, *max = srcset.split(',')
190
+
191
+ # parse out the width descriptor as an integer
192
+ min = min.split(' ')[1].to_i
193
+ max = max[max.length - 1].split(' ')[1].to_i
194
+
195
+ assert_operator min, :>=, 100
196
+ assert_operator max, :<=, 8192
197
+ end
198
+
199
+ # a 17% testing threshold is used to account for rounding
200
+ def test_srcset_iterates_17_percent
201
+ increment_allowed = 0.17
202
+
203
+ # create an array of widths
204
+ widths = srcset.split(',').map do |src|
205
+ src.split(' ')[1].to_i
206
+ end
207
+
208
+ prev = widths[0]
209
+
210
+ for i in 1..widths.length - 1
211
+ element = widths[i]
212
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
213
+ prev = element
214
+ end
215
+ end
216
+
217
+ def test_srcset_signs_urls
218
+ srcset.split(',').map do |srcset_split|
219
+ src = srcset_split.split(' ')[0]
220
+ expected_signature = get_expected_signature(src)
221
+
222
+ assert_includes src, expected_signature
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ def srcset
229
+ @client ||= mock_signed_client.to_srcset(h: 100)
230
+ end
231
+ end
232
+
233
+ class SrcsetGivenWidthAndHeight < Imgix::Test
234
+ include SrcsetTest
235
+
236
+ def test_srcset_in_dpr_form
237
+ device_pixel_ratio = 1
238
+ srcset.split(',').map do |src|
239
+ ratio = src.split(' ')[1]
240
+ assert_equal "#{device_pixel_ratio}x", ratio
241
+ device_pixel_ratio += 1
242
+ end
243
+ end
244
+
245
+ def test_srcset_has_dpr_params
246
+ i = 1
247
+ srcset.split(',').map do |srcset_split|
248
+ src = srcset_split.split(' ')[0]
249
+ assert_includes src, "dpr=#{i}"
250
+ i += 1
251
+ end
252
+ end
253
+
254
+ def test_srcset_signs_urls
255
+ srcset.split(',').map do |srcset_split|
256
+ src = srcset_split.split(' ')[0]
257
+ expected_signature = get_expected_signature(src)
258
+
259
+ assert_includes src, expected_signature
260
+ end
261
+ end
262
+
263
+ def test_srcset_has_variable_qualities
264
+ i = 0
265
+ srcset.split(',').map do |src|
266
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
267
+ i += 1
268
+ end
269
+ end
270
+
271
+ def test_srcset_respects_overriding_quality
272
+ quality_override = 100
273
+ srcset = mock_signed_client.to_srcset(w: 100, h: 100, q: quality_override)
274
+
275
+ srcset.split(',').map do |src|
276
+ assert_includes src, "q=#{quality_override}"
277
+ end
278
+ end
279
+
280
+ def test_disable_variable_quality
281
+ srcset = mock_signed_client.to_srcset(
282
+ w: 100, h: 100,
283
+ options: { disable_variable_quality: true }
284
+ )
285
+
286
+ srcset.split(',').map do |src|
287
+ assert(not(src.include?('q=')))
288
+ end
289
+ end
290
+
291
+ def test_respects_quality_param_when_disabled
292
+ quality_override = 100
293
+ srcset = mock_signed_client.to_srcset(
294
+ w: 100, h: 100, q: 100,
295
+ options: { disable_variable_quality: true }
296
+ )
297
+
298
+ srcset.split(',').map do |src|
299
+ assert_includes src, "q=#{quality_override}"
300
+ end
301
+ end
302
+
303
+ private
304
+
305
+ def srcset
306
+ @client ||= mock_signed_client.to_srcset(w: 100, h: 100)
307
+ end
308
+ end
309
+
310
+ class SrcsetGivenAspectRatio < Imgix::Test
311
+ include SrcsetTest
312
+
313
+ def test_srcset_generates_width_pairs
314
+ expected_number_of_pairs = 31
315
+ assert_equal expected_number_of_pairs, srcset.split(',').length
316
+ end
317
+
318
+ def test_srcset_pair_values
319
+ srclist = srcset.split(',').map do |srcset_split|
320
+ srcset_split.split(' ')[1].to_i
321
+ end
322
+
323
+ for i in 0..srclist.length - 1
324
+ assert_equal(srclist[i], RESOLUTIONS[i])
325
+ end
326
+ end
327
+
328
+ def test_srcset_within_bounds
329
+ min, *max = srcset.split(',')
330
+
331
+ # parse out the width descriptor as an integer
332
+ min = min.split(' ')[1].to_i
333
+ max = max[max.length - 1].split(' ')[1].to_i
334
+
335
+ assert_operator min, :>=, 100
336
+ assert_operator max, :<=, 8192
337
+ end
338
+
339
+ # a 17% testing threshold is used to account for rounding
340
+ def test_srcset_iterates_17_percent
341
+ increment_allowed = 0.17
342
+
343
+ # create an array of widths
344
+ widths = srcset.split(',').map do |src|
345
+ src.split(' ')[1].to_i
346
+ end
347
+
348
+ prev = widths[0]
349
+
350
+ for i in 1..widths.length - 1
351
+ element = widths[i]
352
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
353
+ prev = element
354
+ end
355
+ end
356
+
357
+ def test_srcset_signs_urls
358
+ srcset.split(',').map do |srcset_split|
359
+ src = srcset_split.split(' ')[0]
360
+ expected_signature = get_expected_signature(src)
361
+
362
+ assert_includes src, expected_signature
363
+ end
364
+ end
365
+
366
+ private
367
+
368
+ def srcset
369
+ @client ||= mock_signed_client.to_srcset(ar: '3:2')
370
+ end
371
+ end
372
+
373
+ class SrcsetGivenAspectRatioAndHeight < Imgix::Test
374
+ include SrcsetTest
375
+
376
+ def test_srcset_in_dpr_form
377
+ device_pixel_ratio = 1
378
+
379
+ srcset.split(',').map do |src|
380
+ ratio = src.split(' ')[1]
381
+ assert_equal "#{device_pixel_ratio}x", ratio
382
+ device_pixel_ratio += 1
383
+ end
384
+ end
385
+
386
+ def test_srcset_has_dpr_params
387
+ i = 1
388
+ srcset.split(',').map do |srcset_split|
389
+ src = srcset_split.split(' ')[0]
390
+ assert_includes src, "dpr=#{i}"
391
+ i += 1
392
+ end
393
+ end
394
+
395
+ def test_srcset_signs_urls
396
+ srcset.split(',').map do |srcset_split|
397
+ src = srcset_split.split(' ')[0]
398
+ expected_signature = get_expected_signature(src)
399
+
400
+ assert_includes src, expected_signature
401
+ end
402
+ end
403
+
404
+ def test_srcset_has_variable_qualities
405
+ i = 0
406
+ srcset.split(',').map do |src|
407
+ assert_includes src, "q=#{DPR_QUALITY[i]}"
408
+ i += 1
409
+ end
410
+ end
411
+
412
+ def test_srcset_respects_overriding_quality
413
+ quality_override = 100
414
+ srcset = mock_signed_client.to_srcset(
415
+ w: 100, ar: '3:2', q: quality_override
416
+ )
417
+
418
+ srcset.split(',').map do |src|
419
+ assert_includes src, "q=#{quality_override}"
420
+ end
421
+ end
422
+
423
+ def test_disable_variable_quality
424
+ srcset = mock_signed_client.to_srcset(
425
+ w: 100, ar: '3:2',
426
+ options: { disable_variable_quality: true }
427
+ )
428
+
429
+ srcset.split(',').map do |src|
430
+ assert(not(src.include?('q=')))
431
+ end
432
+ end
433
+
434
+ def test_respects_quality_param_when_disabled
435
+ quality_override = 100
436
+ srcset = mock_signed_client.to_srcset(
437
+ w: 100, h: 100, q: 100,
438
+ options: { disable_variable_quality: true }
439
+ )
440
+
441
+ srcset.split(',').map do |src|
442
+ assert_includes src, "q=#{quality_override}"
443
+ end
444
+ end
445
+
446
+ private
447
+
448
+ def srcset
449
+ @client ||= mock_signed_client.to_srcset({ h: 100, ar: '3:2' })
450
+ end
451
+ end
452
+
453
+ class SrcsetWidthTolerance < Imgix::Test
454
+ include SrcsetTest
455
+
456
+ def test_srcset_generates_width_pairs
457
+ expected_number_of_pairs = 15
458
+ assert_equal expected_number_of_pairs, srcset.split(',').length
459
+ end
460
+
461
+ def test_srcset_pair_values
462
+ resolutions = [100, 140, 196, 274, 384,
463
+ 538, 753, 1054, 1476, 2066,
464
+ 2893, 4050, 5669, 7937, 8192]
465
+
466
+ srclist = srcset.split(',').map do |srcset_split|
467
+ srcset_split.split(' ')[1].to_i
468
+ end
469
+
470
+ for i in 0..srclist.length - 1
471
+ assert_equal(srclist[i], resolutions[i])
472
+ end
473
+ end
474
+
475
+ def test_srcset_within_bounds
476
+ min, *max = srcset.split(',')
477
+
478
+ # parse out the width descriptor as an integer
479
+ min = min.split(' ')[1].to_i
480
+ max = max[max.length - 1].split(' ')[1].to_i
481
+ assert_operator min, :>=, 100
482
+ assert_operator max, :<=, 8192
483
+ end
484
+
485
+ # a 41% testing threshold is used to account for rounding
486
+ def test_srcset_iterates_41_percent
487
+ increment_allowed = 0.41
488
+
489
+ # create an array of widths
490
+ widths = srcset.split(',').map do |src|
491
+ src.split(' ')[1].to_i
492
+ end
493
+
494
+ prev = widths[0]
495
+
496
+ for i in 1..widths.length - 1
497
+ element = widths[i]
498
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
499
+ prev = element
500
+ end
501
+ end
502
+
503
+ def test_invalid_tolerance_emits_error
504
+ assert_raises(ArgumentError) do
505
+ mock_client.to_srcset(options: { width_tolerance: 'abc' })
506
+ end
507
+ end
508
+
509
+ def test_negative_tolerance_emits_error
510
+ assert_raises(ArgumentError) do
511
+ mock_client.to_srcset(options: { width_tolerance: -0.10 })
512
+ end
513
+ end
514
+
515
+ def test_with_param_after
516
+ srcset = mock_signed_client.to_srcset(
517
+ options: { width_tolerance: 0.20 },
518
+ h: 1000, fit: 'clip'
519
+ )
520
+
521
+ assert_includes(srcset, 'h=')
522
+ assert(not(srcset.include?('width_tolerance=')))
523
+ end
524
+
525
+ def test_with_param_before
526
+ srcset = mock_signed_client.to_srcset(
527
+ h: 1000, fit: 'clip',
528
+ options: { width_tolerance: 0.20 }
529
+ )
530
+
531
+ assert_includes(srcset, 'h=')
532
+ assert(not(srcset.include?('width_tolerance=')))
533
+ end
534
+
535
+ private
536
+
537
+ def srcset
538
+ @client ||= mock_signed_client.to_srcset(
539
+ options: { width_tolerance: 0.20 }
540
+ )
541
+ end
542
+ end
543
+
544
+ class SrcsetCustomWidths < Imgix::Test
545
+ include SrcsetTest
546
+
547
+ def test_srcset_generates_width_pairs
548
+ expected_number_of_pairs = 4
549
+ assert_equal expected_number_of_pairs, srcset.split(',').length
550
+ end
551
+
552
+ def test_srcset_pair_values
553
+ resolutions = [100, 500, 1000, 1800]
554
+ srclist = srcset.split(',').map do |srcset_split|
555
+ srcset_split.split(' ')[1].to_i
556
+ end
557
+
558
+ for i in 0..srclist.length - 1
559
+ assert_equal(srclist[i], resolutions[i])
560
+ end
561
+ end
562
+
563
+ def test_srcset_within_bounds
564
+ min, *max = srcset.split(',')
565
+
566
+ # parse out the width descriptor as an integer
567
+ min = min.split(' ')[1].to_i
568
+ max = max[max.length - 1].split(' ')[1].to_i
569
+
570
+ assert_operator min, :>=, @widths[0]
571
+ assert_operator max, :<=, @widths[-1]
572
+ end
573
+
574
+ def test_invalid_widths_input_emits_error
575
+ assert_raises(ArgumentError) do
576
+ mock_client.to_srcset(options: { widths: 'abc' })
577
+ end
578
+ end
579
+
580
+ def test_non_integer_array_emits_error
581
+ assert_raises(ArgumentError) do
582
+ mock_client.to_srcset(options: { widths: [100, 200, false] })
583
+ end
584
+ end
585
+
586
+ def test_negative_integer_array_emits_error
587
+ assert_raises(ArgumentError) do
588
+ mock_client.to_srcset(options: { widths: [100, 200, -100] })
589
+ end
590
+ end
591
+
592
+ def test_with_param_after
593
+ srcset = mock_signed_client.to_srcset(
594
+ options: { widths: [100, 200, 300] },
595
+ h: 1000, fit: 'clip'
596
+ )
597
+
598
+ assert_includes(srcset, 'h=')
599
+ assert(not(srcset.include?('widths=')))
600
+ end
601
+
602
+ def test_with_param_before
603
+ srcset = mock_client.to_srcset(
604
+ h: 1000, fit: 'clip',
605
+ options: { widths: [100, 200, 300] }
606
+ )
607
+ assert_includes(srcset, 'h=')
608
+ assert(not(srcset.include?('widths=')))
609
+ end
610
+
611
+ private
612
+
613
+ def srcset
614
+ @widths = [100, 500, 1000, 1800]
615
+ @client ||= mock_signed_client.to_srcset(options: { widths: @widths })
616
+ end
617
+ end
618
+
619
+ class SrcsetMinMaxWidths < Imgix::Test
620
+ include SrcsetTest
621
+
622
+ def test_srcset_generates_width_pairs
623
+ expected_number_of_pairs = 11
624
+ assert_equal expected_number_of_pairs, srcset.split(',').length
625
+ end
626
+
627
+ def test_srcset_pair_values
628
+ resolutions = [500, 580, 673, 780, 905, 1050,
629
+ 1218, 1413, 1639, 1901, 2000]
630
+ srclist = srcset.split(',').map do |srcset_split|
631
+ srcset_split.split(' ')[1].to_i
632
+ end
633
+
634
+ for i in 0..srclist.length - 1
635
+ assert_equal(srclist[i], resolutions[i])
636
+ end
637
+ end
638
+
639
+ def test_srcset_within_bounds
640
+ min, *max = srcset.split(',')
641
+
642
+ # parse out the width descriptor as an integer
643
+ min = min.split(' ')[1].to_i
644
+ max = max[max.length - 1].split(' ')[1].to_i
645
+
646
+ assert_operator min, :>=, @MIN
647
+ assert_operator max, :<=, @MAX
648
+ end
649
+
650
+ # a 41% testing threshold is used to account for rounding
651
+ def test_with_custom_width_tolerance
652
+ srcset = mock_client.to_srcset(
653
+ options: { min_width: 500, max_width: 2000, width_tolerance: 0.20 }
654
+ )
655
+
656
+ increment_allowed = 0.41
657
+
658
+ # create an array of widths
659
+ widths = srcset.split(',').map do |src|
660
+ src.split(' ')[1].to_i
661
+ end
662
+
663
+ prev = widths[0]
664
+
665
+ for i in 1..widths.length - 1
666
+ element = widths[i]
667
+ assert_operator (element.to_f / prev.to_f), :<, (1 + increment_allowed)
668
+ prev = element
669
+ end
670
+ end
671
+
672
+ def test_invalid_min_emits_error
673
+ assert_raises(ArgumentError) do
674
+ mock_client.to_srcset(options: { min_width: 'abc' })
675
+ end
676
+ end
677
+
678
+ def test_negative_max_emits_error
679
+ assert_raises(ArgumentError) do
680
+ mock_client.to_srcset(options: { max_width: -100 })
681
+ end
682
+ end
683
+
684
+ def test_with_param_after
685
+ srcset = mock_client.to_srcset(
686
+ options: { min_width: 500, max_width: 2000 },
687
+ h: 1000, fit: 'clip'
688
+ )
689
+
690
+ assert_includes(srcset, 'h=')
691
+ assert(not(srcset.include?('min_width=')))
692
+ assert(not(srcset.include?('max_width=')))
693
+ end
694
+
695
+ def test_with_param_before
696
+ srcset = mock_client.to_srcset(
697
+ h: 1000, fit: 'clip',
698
+ options: { min_width: 500, max_width: 2000 }
699
+ )
700
+
701
+ assert_includes(srcset, 'h=')
702
+ assert(not(srcset.include?('min_width=')))
703
+ assert(not(srcset.include?('max_width=')))
704
+ end
705
+
706
+ def test_only_min
707
+ min_width = 1000
708
+ max_width = 8192
709
+ srcset = mock_client.to_srcset(options: { min_width: min_width })
710
+
711
+ min, *max = srcset.split(',')
712
+
713
+ # parse out the width descriptor as an integer
714
+ min = min.split(' ')[1].to_i
715
+ max = max[max.length - 1].split(' ')[1].to_i
716
+
717
+ assert_operator min, :>=, min_width
718
+ assert_operator max, :<=, max_width
719
+ end
720
+
721
+ def test_only_max
722
+ min_width = 100
723
+ max_width = 1000
724
+ srcset = mock_client.to_srcset(options: { max_width: max_width })
725
+ min, *max = srcset.split(',')
726
+
727
+ # parse out the width descriptor as an integer
728
+ min = min.split(' ')[1].to_i
729
+ max = max[max.length - 1].split(' ')[1].to_i
730
+
731
+ assert_operator min, :>=, min_width
732
+ assert_operator max, :<=, max_width
733
+ end
734
+
735
+ def test_max_as_100
736
+ srcset = mock_client.to_srcset(options: { max_width: 100 })
737
+ assert_equal(srcset, 'https://testing.imgix.net/image.jpg?w=100 100w')
738
+ end
739
+
740
+ def test_min_as_8192
741
+ srcset = mock_client.to_srcset(options: { min_width: 8192 })
742
+ assert_equal(srcset, 'https://testing.imgix.net/image.jpg?w=8192 8192w')
743
+ end
744
+
745
+ private
746
+
747
+ def srcset
748
+ @MIN = 500
749
+ @MAX = 2000
750
+ @client ||= mock_client.to_srcset(
751
+ options: { min_width: @MIN, max_width: @MAX }
752
+ )
753
+ end
754
+ end
755
+ end