ballonizer 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,7 +14,9 @@
14
14
  background-color: white;
15
15
  color: black;
16
16
  text-align: center;
17
- overflow: hidden;
17
+ display: block;
18
+ word-wrap: break-word;
19
+ overflow: hidden;
18
20
  }
19
21
 
20
22
  .ballonizer_ballon_hidden_for_edition
@@ -31,44 +33,47 @@
31
33
  * occupy space (no one). With this the form is "hidden" because don't occupy
32
34
  * any space (the width AND height are equal zero).
33
35
  */
34
- .ballonizer_image_form
36
+ .ballonizer_image_form, .ballonizer_image_form div
35
37
  {
36
38
  margin: 0;
37
39
  padding: 0;
38
40
  border: 0;
41
+ left: 0;
42
+ top: 0;
39
43
  display: inline-block;
44
+ /* we must */
45
+ position: relative;
40
46
  }
41
47
 
42
- .ballonizer_image_form > *
48
+ .ballonizer_image_form .ballonizer_edition_ballon
43
49
  {
44
50
  position: absolute;
45
51
  display: none;
52
+ border: 0;
46
53
  margin: 0;
47
54
  text-align: center;
48
55
  }
49
56
 
50
- .ballonizer_image_form > .ballonizer_ballon_in_edition
57
+ .ballonizer_image_form .ballonizer_edition_ballon.ballonizer_ballon_in_edition
51
58
  {
52
59
  display: block;
53
60
  }
54
61
 
55
62
  /* We use the same trick used in the image form in the page form */
56
- .ballonizer_page_form
63
+ .ballonizer_page_form, .ballonizer_page_form div
57
64
  {
58
65
  display: inline-block;
59
- padding: 0;
60
66
  margin: 0;
61
- border: 0;
67
+ padding: 0;
68
+ border: 0;
62
69
  }
63
70
 
64
- .ballonizer_page_form > *
71
+ .ballonizer_page_form input[type=submit]
65
72
  {
66
- display: none;
67
- margin: 0;
68
- padding: 0;
73
+ display: none;
69
74
  }
70
75
 
71
- .ballonizer_page_form > .ballonizer_ballons_have_changes
76
+ .ballonizer_page_form input[type=submit].ballonizer_ballons_have_changes
72
77
  {
73
78
  display: block;
74
79
  position: fixed;
data/lib/ballonizer.rb CHANGED
@@ -54,6 +54,24 @@ require 'sprockets'
54
54
  # The tables names are: images, ballons, ballonized_image_versions,
55
55
  # ballonized_image_ballons.
56
56
  #
57
+ # Changelog:
58
+ # v0.4.0:
59
+ # * Changed the way the Javascript module add containers in the page
60
+ # to avoid creating invalid HTML4.0.1/XHTML1.1/HTML5 documents.
61
+ # * Now the ballonize_page takes a mime-type argument to decide if
62
+ # the page has to be parsed as XML or HTML (trying to be in
63
+ # conformance with http://www.w3.org/TR/xhtml-media-types/).
64
+ # * The change in the ballon size now change the font-size of the
65
+ # ballon text.
66
+ # * Database schema change, as consequence of the font-size change,
67
+ # the database now stores the font-size. No migration provided for
68
+ # databases in the old format, but the font-size field can be null.
69
+ # The migration only require adding this column with null value to
70
+ # all records (see the create_tables code).
71
+ # * Fixed a bug in the Javascript module that give wrong position and
72
+ # size values to all ballons that aren't edited/added before submmiting
73
+ # (only if the image wasn't loaded before the javascript loading).
74
+ #
57
75
  class Ballonizer
58
76
 
59
77
  # The superclass of any error explicitly raised by the Ballonizer class.
@@ -75,22 +93,38 @@ class Ballonizer
75
93
  e.freeze
76
94
  end
77
95
 
78
- def self.parse_html_or_xhtml(doc)
79
- # If you parse XHTML as HTML with Nokogiri and use to_s after the markup can be messed up
96
+ def self.parse_html_or_xhtml(doc, mime_type)
97
+ # If you parse XHTML as HTML with Nokogiri, and use to_s after, the markup
98
+ # can be messed up, breaking the structural integrity of the xml
80
99
  #
81
100
  # Example: <meta name="description" content="not important" />
82
101
  # becomes <meta name="description" content="not important" >
83
- # To avoid this we parse a document that is XML valid as XML, and, otherwise as HTML
102
+ #
103
+ # In the other side if you parse HTML as a XML, and use to_s after, the
104
+ # Nokogiri make empty content tags self-close
105
+ #
106
+ # Example: <script type="text/javascript" src="/ballonizer.js"></script>
107
+ # becomes: <script type="text/javascript" src="/ballonizer.js" />
108
+ #
109
+ # What's even worse than the contrary (xml as html)
84
110
  parsed_doc = nil
85
- begin
86
- # this also isn't a great way to do this
87
- # the Nokogiri don't have exception classes, this way any StandardError will be silenced
111
+
112
+ case mime_type
113
+ when /text\/html/
114
+ parsed_doc = Nokogiri::HTML(doc)
115
+ when /application\/xhtml\+xml/
88
116
  options = Nokogiri::XML::ParseOptions::DEFAULT_XML &
89
117
  Nokogiri::XML::ParseOptions::STRICT &
90
118
  Nokogiri::XML::ParseOptions::NONET
91
- parsed_doc = Nokogiri::XML::Document.parse(doc, nil, nil, options)
92
- rescue
93
- parsed_doc = Nokogiri::HTML(doc)
119
+ begin
120
+ parsed_doc = Nokogiri::XML::Document.parse(doc, nil, nil, options)
121
+ rescue
122
+ return nil
123
+ end
124
+ else
125
+ fail Error, "the only mime-types accepted are text/html and" +
126
+ " application/xhtml+xml, the passed argument was " +
127
+ "'#{mime_type}'"
94
128
  end
95
129
 
96
130
  parsed_doc
@@ -258,12 +292,16 @@ class Ballonizer
258
292
  if ballon["text"].empty?
259
293
  fail SubmitError, "the ballon text is empty"
260
294
  end
295
+ [:top, :left, :width, :height, :font_size].each do | numeric_attr_name |
296
+ numeric_attr = ballon[numeric_attr_name.to_s]
297
+ unless numeric_attr.is_a?(Fixnum) || numeric_attr.is_a?(Float)
298
+ fail SubmitError, "the #{numeric_attr_name} " +
299
+ "(#{numeric_attr}) isn't a Fixnum or " +
300
+ "Float (is a '#{numeric_attr.class}')"
301
+ end
302
+ end
261
303
  [:top, :left, :width, :height].each do | bound_name |
262
304
  bound = ballon[bound_name.to_s]
263
- unless bound.is_a?(Fixnum) || bound.is_a?(Float)
264
- fail SubmitError, "the #{bound_name.to_s} (#{bound.to_s}) isn't" +
265
- " a Fixnum or Float (is a '#{bound.class.to_s}')"
266
- end
267
305
  unless bound >= 0 && bound <= 1
268
306
  fail SubmitError, "the #{bound_name.to_s} (#{bound.to_s}) isn't"
269
307
  " between 0 and 1 (both inclusive)"
@@ -271,14 +309,14 @@ class Ballonizer
271
309
  end
272
310
 
273
311
  ballon_end = {}
274
- ballon_end[:x] = ballon["top"] + ballon["height"]
275
- ballon_end[:y] = ballon["left"] + ballon["width"]
312
+ ballon_end[:x] = ballon["left"] + ballon["width"]
313
+ ballon_end[:y] = ballon["top"] + ballon["height"]
276
314
 
277
315
  [:x, :y].each do | axis |
278
316
  if ballon_end[axis] > 1
279
- fail SubmitError, "the ballon with text #{ballon["text"]} is trespassing" +
280
- " the #{ {x: "right side", y: "bottom"} [axis] }" +
281
- " of the image"
317
+ side = { x: "right side", y: "bottom" }[axis]
318
+ fail SubmitError, "the ballon with text #{ballon["text"].to_s} " +
319
+ "is trespassing the #{side} of the image"
282
320
  end
283
321
  end
284
322
  end
@@ -351,22 +389,35 @@ class Ballonizer
351
389
  # Wrap each image to ballonize with a container, add its ballons to the
352
390
  # container and, possibly, add the css and js libs and snippet for the
353
391
  # edition initialization. Don't make any change if the page has no images
354
- # to ballonize.
392
+ # to ballonize. If the page can't be parsed (as HTML or X(HT)ML, depending
393
+ # of the mime-type) return the page argument without throwing any exceptions.
394
+ # Throw an exception if the mime-type doesn't match with html or xhtml.
355
395
  # @param page [String] The (X)HTML page.
356
396
  # @param page_url [String] The url of the page to be ballonized, necessary
357
397
  # to make absolute the src attribute of img (if it's relative).
358
398
  # @param settings [Hash{Symbol => String}] Optional. If not provided the
359
399
  # #settings will be used.
360
- # @return [String] The page ballonized (new string).
361
- def ballonize_page(page, page_url, settings = {})
400
+ # @param mime_type A string that have the substring 'text/html' or
401
+ # 'application/xhtml+xml'.
402
+ # @return [String] The ballonized page (new string), or the same string,
403
+ # if the parse has failed.
404
+ # @raise [Ballonizer::Error] If the mime-type don't match either 'text/html'
405
+ # or 'application/xhtml+xml'.
406
+ def ballonize_page(page, page_url, mime_type, settings = {})
362
407
  settings = self.settings.merge(settings)
363
408
 
364
- parsed_page = Workaround.parse_html_or_xhtml(page)
409
+ # can raise Ballonizer::Error if the mime-type is invalid
410
+ parsed_page = Workaround.parse_html_or_xhtml(page, mime_type)
411
+ # if can't parse return the page unaltered
412
+ if parsed_page.nil?
413
+ return page
414
+ end
415
+
365
416
  selector = settings[:img_to_ballonize_css_selector]
366
417
  imgs = parsed_page.css(selector)
367
418
 
368
419
  unless imgs.empty?
369
- imgs.wrap('<div class="ballonizer_image_container" ></div>')
420
+ imgs.wrap('<span class="ballonizer_image_container" ></span>')
370
421
 
371
422
  imgs.each do | img |
372
423
  img_src = img['src']
@@ -403,8 +454,9 @@ class Ballonizer
403
454
  # transform ratio [0,1] to percent [0, 100]
404
455
  style = style + "#{sym}: #{(ballon_data[sym] * 100)}%;"
405
456
  end
457
+ style = style + "font-size: #{ballon_data[:font_size]}px;"
406
458
 
407
- "<p class='ballonizer_ballon' style='#{style}'>#{text}</p>"
459
+ "<span class='ballonizer_ballon' style='#{style}'>#{text}</span>"
408
460
  end
409
461
 
410
462
  # @api private
@@ -422,7 +474,8 @@ class Ballonizer
422
474
  .join(:ballons, { ballonized_image_ballons__version: version,
423
475
  ballonized_image_ballons__image_id: image_id,
424
476
  ballonized_image_ballons__ballon_id: :ballons__id
425
- }).select(:text, :top, :left, :width, :height).all
477
+ }).select(:text, :top, :left, :width, :height,
478
+ :font_size).all
426
479
  else
427
480
  []
428
481
  end
@@ -458,6 +511,9 @@ class Ballonizer
458
511
  Float :left, :allow_null => false
459
512
  Float :width, :allow_null => false
460
513
  Float :height, :allow_null => false
514
+ # the font_size allow null to support databases migrated from old versions
515
+ # (that don't have this field)
516
+ Float :font_size, :allow_null => true
461
517
  end
462
518
  db.create_table(:ballonized_image_versions) do
463
519
  Integer :version
@@ -31,6 +31,24 @@ RSpec::Matchers.define :exist_in_filesystem do
31
31
  "be a list of absolute paths for existing files or directories"
32
32
  end
33
33
  end
34
+ # TODO: check the font-size, the position and the size of the ballon
35
+ # TODO: check the src of the image inside the container (not so easy
36
+ # because the src is relative in the html but absolute in the database)
37
+ RSpec::Matchers.define :have_ballons_as_submitted_by do | expected |
38
+ match do | actual |
39
+ expected.each do | img_src, ballons |
40
+ expect(actual).to(have_tag('.ballonizer_image_container') do
41
+ with_tag('img')
42
+ ballons.each do | b |
43
+ with_tag('span', { text: b['text'], with: { class: 'ballonizer_ballon' }})
44
+ end
45
+ end)
46
+ end
47
+ end
48
+ description do
49
+ 'have ballons as the ones added by the last submit'
50
+ end
51
+ end
34
52
 
35
53
  describe Ballonizer do
36
54
 
@@ -42,17 +60,23 @@ describe Ballonizer do
42
60
  db[:images].insert({img_src: 'http://comic-translation.com/tr/pt-BR/a_comic/imgs/3.jpg'})
43
61
 
44
62
  db[:ballons].insert({ text: 'first ballon of first image',
45
- top: 0, left: 0, width: 0.5, height: 0.5 })
63
+ top: 0, left: 0, width: 0.5, height: 0.5,
64
+ font_size: 16 })
46
65
  db[:ballons].insert({ text: 'second ballon of first image',
47
- top: 0.5, left: 0.5, width: 0.5, height: 0.5 })
66
+ top: 0, left: 0, width: 0.5, height: 0.5,
67
+ font_size: 16 })
48
68
  db[:ballons].insert({ text: 'first ballon of second image',
49
- top: 0, left: 0, width: 0.5, height: 0.5 })
69
+ top: 0, left: 0, width: 0.5, height: 0.5,
70
+ font_size: 16 })
50
71
  db[:ballons].insert({ text: 'second ballon of second image',
51
- top: 0.5, left: 0.5, width: 0.5, height: 0.5 })
72
+ top: 0, left: 0, width: 0.5, height: 0.5,
73
+ font_size: 16 })
52
74
  db[:ballons].insert({ text: 'first ballon of third image',
53
- top: 0, left: 0, width: 0.5, height: 0.5 })
75
+ top: 0, left: 0, width: 0.5, height: 0.5,
76
+ font_size: 16 })
54
77
  db[:ballons].insert({ text: 'second ballon of third image',
55
- top: 0.5, left: 0.5, width: 0.5, height: 0.5 })
78
+ top: 0, left: 0, width: 0.5, height: 0.5,
79
+ font_size: 16 })
56
80
 
57
81
  # both ballons added in the first version
58
82
  db[:ballonized_image_versions].insert({image_id: 1, version: 1, time: time})
@@ -81,6 +105,10 @@ describe Ballonizer do
81
105
  JSON.parse(JSON.generate(v))
82
106
  end
83
107
 
108
+ # definitions to be overriden in need, but who doesn't need a *_example
109
+ # counterpart (are so simple that clone and change isn't pratical)
110
+ let (:mime_type) { 'application/xhtml+xml' }
111
+
84
112
  # Definitions ending with '_example' are to be cloned and defined in a
85
113
  # context without the sufix. Definitions without the sufix are used in the
86
114
  # specs and may require the definition of some without '_example' counterparts.
@@ -139,7 +167,7 @@ describe Ballonizer do
139
167
  {}
140
168
  end
141
169
  let (:submit_json_example) do
142
- '{"http://imgs.xkcd.com/comics/cells.png":[{"left":0,"top":0,"width":1,"height":0.23837209302325582,"text":"When you see a claim that a common drug or vitamin \"kills cancer cells in a petri dish\", keep in mind:"},{"left":0.0963302752293578,"top":0.9273255813953488,"width":0.7798165137614679,"height":0.055232558139534885,"text":"So does a handgun."}]}'
170
+ '{"http://imgs.xkcd.com/comics/cells.png":[{"left":0,"top":0,"width":1,"height":0.23837209302325582,"font_size":15,"text":"When you see a claim that a common drug or vitamin \"kills cancer cells in a petri dish\", keep in mind:"},{"left":0.0963302752293578,"top":0.9273255813953488,"width":0.7798165137614679,"height":0.055232558139534885,"font_size":16,"text":"So does a handgun."}]}'
143
171
  end
144
172
  let (:submit_hash_example) do
145
173
  JSON.parse(submit_json_example)
@@ -158,14 +186,53 @@ describe Ballonizer do
158
186
 
159
187
  # Definition who need others (no *_example)
160
188
  let (:instance) { described_class.new(*ballonizer_new_args) }
189
+ let (:ballonize_page_call) do
190
+ lambda { instance.ballonize_page(original_page, page_url, mime_type, settings) }
191
+ end
161
192
  let (:ballonized_page) do
162
- instance.ballonize_page(original_page, page_url, settings)
193
+ ballonize_page_call.call
163
194
  end
164
195
 
165
196
  # TODO: verify if the style property has the correct values
166
197
  describe '#ballonize_page' do
167
198
  subject { ballonized_page }
168
199
 
200
+ context "when the mime-type isn't valid" do
201
+ let (:mime_type) { 'a invalid mime-type' }
202
+ it { expect(ballonize_page_call).to raise_error(Ballonizer::Error) }
203
+ end
204
+ context "when the mime-type is valid" do
205
+ context '(text/html)' do
206
+ let (:mime_type) { 'text/html; charset=utf8' }
207
+ it { expect(ballonize_page_call).to_not raise_error }
208
+ end
209
+ context '(application/xhtml+xml)' do
210
+ let (:mime_type) { 'application/xhtml+xml; charset=utf8' }
211
+ it { expect(ballonize_page_call).to_not raise_error }
212
+ end
213
+ end
214
+ context "when the mime-type is 'application/xhtml+xml'" do
215
+ context "but the page isn't a xml" do
216
+ let (:original_page) do
217
+ <<-END
218
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
219
+ "http://www.w3.org/TR/html4/strict.dtd">
220
+ <html>
221
+ <head>
222
+ <title>A title</title>
223
+ <!-- this isn't a valid xml because the meta tag isn't closed -->
224
+ <meta http-equiv="content-type" content="text/html" charset=UTF-8">
225
+ </head>
226
+ <body>
227
+ </body>
228
+ </html>
229
+ END
230
+ end
231
+ it 'return the original (not cloned) page argument, unmodified' do
232
+ expect(ballonized_page).to be(original_page)
233
+ end
234
+ end
235
+ end
169
236
  context 'when the page has no img elements to ballonize' do
170
237
  it "don't make changes in the page" do
171
238
  should be_equivalent_to(original_page)
@@ -181,7 +248,7 @@ describe Ballonizer do
181
248
  page_with_images.to_s
182
249
  end
183
250
  it 'add a container around the img' do
184
- should have_tag('div', :with => { class: 'ballonizer_image_container' }) do
251
+ should have_tag('span', :with => { class: 'ballonizer_image_container' }) do
185
252
  with_tag('img', :with => { alt: 'A test image' })
186
253
  end
187
254
  end
@@ -189,16 +256,15 @@ describe Ballonizer do
189
256
  it 'add the ballons inside the container' do
190
257
  # the parentheses of the 'should' are necessary, otherwise the
191
258
  # conditions inside the block are silently not tested
192
- should(have_tag('div', :with => { class: 'ballonizer_image_container' }) do
259
+ should(have_tag('span', :with => { class: 'ballonizer_image_container' }) do
193
260
  with_tag("img[alt='A test image']")
194
- with_tag('p', { text: 'first ballon of first image',
261
+ with_tag('span', { text: 'first ballon of first image',
195
262
  with: { class: 'ballonizer_ballon' }})
196
- with_tag('p', { text: 'second ballon of first image',
263
+ with_tag('span', { text: 'second ballon of first image',
197
264
  with: { class: 'ballonizer_ballon'}})
198
265
  end)
199
266
  end
200
267
  end
201
- # TODO: create this specs
202
268
  context 'and the settings define to insert css' do
203
269
  let (:ballonizer_settings) do
204
270
  ballonizer_settings_example.merge({
@@ -241,10 +307,10 @@ describe Ballonizer do
241
307
  end
242
308
  it 'add a container around the imgs' do
243
309
  # TODO: DRY this test
244
- should(have_tag('div', :with => { class: 'ballonizer_image_container' }) do
310
+ should(have_tag('span', :with => { class: 'ballonizer_image_container' }) do
245
311
  with_tag('img', :with => { alt: 'the second test image' })
246
312
  end)
247
- should(have_tag('div', :with => { class: 'ballonizer_image_container' }) do
313
+ should(have_tag('span', :with => { class: 'ballonizer_image_container' }) do
248
314
  with_tag('img', :with => { alt: 'the third test image' })
249
315
  end)
250
316
  end
@@ -252,24 +318,24 @@ describe Ballonizer do
252
318
  it 'add the ballons inside the containers' do
253
319
  # TODO: break this spec in smaller parts, this specificate more
254
320
  # than one thing
255
- should(have_tag('div', :with => { class: 'ballonizer_image_container' }) do
321
+ should(have_tag('span', :with => { class: 'ballonizer_image_container' }) do
256
322
  # the second image have two versions, the second ballon is added
257
323
  # in the second version, so here we verify if the ballonize_page
258
324
  # recover the ballons of the last version
259
325
  with_tag('img', :with => { alt: 'the second test image' })
260
- with_tag('p', { text: 'first ballon of second image',
326
+ with_tag('span', { text: 'first ballon of second image',
261
327
  with: { class: 'ballonizer_ballon' }})
262
- with_tag('p', { text: 'second ballon of second image',
328
+ with_tag('span', { text: 'second ballon of second image',
263
329
  with: { class: 'ballonizer_ballon' }})
264
330
  end)
265
- should(have_tag('div', :with => { class: 'ballonizer_image_container' }) do
331
+ should(have_tag('span', :with => { class: 'ballonizer_image_container' }) do
266
332
  # the third image have two versions, the second ballon is removed
267
333
  # in the second version, so here we verify if the ballonize_page
268
334
  # do not use a ballon of an old version in the image
269
335
  with_tag('img', :with => { alt: 'the third test image' })
270
- with_tag('p', { text: 'first ballon of third image',
336
+ with_tag('span', { text: 'first ballon of third image',
271
337
  with: { class: 'ballonizer_ballon' }})
272
- without_tag('p', { text: 'second ballon of third image',
338
+ without_tag('span', { text: 'second ballon of third image',
273
339
  with: { class: 'ballonizer_ballon' }})
274
340
  end)
275
341
  end
@@ -283,7 +349,7 @@ describe Ballonizer do
283
349
  page_with_images.to_s
284
350
  end
285
351
  it "don't add a container around the imgs" do
286
- should_not have_tag('div', :with => {
352
+ should_not have_tag('span', :with => {
287
353
  class: 'ballonizer_image_container'
288
354
  })
289
355
  end
@@ -437,6 +503,17 @@ describe Ballonizer do
437
503
  end
438
504
  end
439
505
 
506
+ context "when a ballon don't have font_size" do
507
+ let (:submit_hash) do
508
+ deep_copy(submit_hash_example)[submit_hash_example.keys.first]
509
+ .first.update({ font_size: nil })
510
+ end
511
+
512
+ it { should be_false }
513
+
514
+ include_examples 'and the second argument is true'
515
+ end
516
+
440
517
  context 'when the submit contain a image without ballons' do
441
518
  let (:submit_hash) do
442
519
  { submit_hash_example.keys.first => [] }
@@ -444,6 +521,7 @@ describe Ballonizer do
444
521
 
445
522
  it { should be_true }
446
523
  end
524
+
447
525
  context 'when the hash is valid' do
448
526
  it { should be_true }
449
527
 
@@ -480,14 +558,7 @@ describe Ballonizer do
480
558
 
481
559
  it 'the ballonize_page add the ballons to the image' do
482
560
  instance.process_submit_hash(submit_hash, Time.at(0))
483
- expect(ballonized_page).to have_tag('p',
484
- { text: 'the first ballon of the fourth image',
485
- with: { class: 'ballonizer_ballon' } }
486
- )
487
- expect(ballonized_page).to have_tag('p',
488
- { text: 'the second ballon of the fourth image',
489
- with: { class: 'ballonizer_ballon' } }
490
- )
561
+ expect(ballonized_page).to have_ballons_as_submitted_by(submit_hash)
491
562
  end
492
563
  end
493
564
  context 'when the submit refer to a image already with ballons' do
@@ -512,14 +583,7 @@ describe Ballonizer do
512
583
 
513
584
  it 'the ballonize_page use the new ballons' do
514
585
  instance.process_submit_hash(submit_hash, Time.at(0))
515
- expect(ballonized_page).to have_tag('p',
516
- { text: 'the first ballon (version 2) of the first image',
517
- with: { class: 'ballonizer_ballon' } }
518
- )
519
- expect(ballonized_page).to have_tag('p',
520
- { text: 'the second ballon (version 2) of the first image',
521
- with: { class: 'ballonizer_ballon' } }
522
- )
586
+ expect(ballonized_page).to have_ballons_as_submitted_by(submit_hash)
523
587
  end
524
588
  end
525
589
  end
@@ -549,9 +613,10 @@ describe Ballonizer do
549
613
  # The submit hash is used to define the env_example
550
614
  # (and in consequence the env)
551
615
  let (:submit_hash) do
552
- # this input is invalid because the text field can't be empty
616
+ # this input is invalid because have no font_size
553
617
  { 'http://comic-translation.com/tr/pt-BR/a_comic/imgs/4.jpg' => [
554
- { 'text' => '', 'left' => 0, 'top' => 0, 'width' => 0.5, 'height' => 0.5 }
618
+ { 'text' => 'test', 'left' => 0, 'top' => 0, 'width' => 0.5,
619
+ 'height' => 0.5 }
555
620
  ]}
556
621
  end
557
622
 
@@ -568,7 +633,8 @@ describe Ballonizer do
568
633
  let(:submit_hash) do
569
634
  { 'http://comic-translation.com/tr/pt-BR/a_comic/imgs/4.jpg' => [
570
635
  { 'text' => 'the first ballon of the fourth image',
571
- 'left' => 0, 'top' => 0, 'width' => 0.5, 'height' => 0.5 }
636
+ 'left' => 0, 'top' => 0, 'width' => 0.5, 'height' => 0.5,
637
+ 'font_size' => 16 }
572
638
  ]}
573
639
  end
574
640
 
@@ -581,10 +647,7 @@ describe Ballonizer do
581
647
 
582
648
  it 'the ballonize_page add the ballons to the image' do
583
649
  instance.process_submit(env)
584
- expect(ballonized_page).to have_tag('p',
585
- { text: 'the first ballon of the fourth image',
586
- with: { class: 'ballonizer_ballon' } }
587
- )
650
+ expect(ballonized_page).to have_ballons_as_submitted_by(submit_hash)
588
651
  end
589
652
  end
590
653
  end