sablon 0.0.22 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
Binary file
@@ -34,11 +34,6 @@ class HTMLConverterASTBuilderTest < Sablon::TestCase
34
34
  @bulider.send(:validate_structure, span, div)
35
35
  end
36
36
  assert_equal "Invalid HTML structure: div is not a valid child element of span.", e.message
37
- # test inline tag with no parent
38
- e = assert_raises ArgumentError do
39
- @bulider.send(:validate_structure, root, span)
40
- end
41
- assert_equal "Invalid HTML structure: span needs to be wrapped in a block level tag.", e.message
42
37
  end
43
38
 
44
39
  def test_merge_properties
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "test_helper"
3
+ require 'securerandom'
3
4
 
4
5
  class HTMLConverterASTTest < Sablon::TestCase
5
6
  def setup
@@ -32,6 +33,12 @@ class HTMLConverterASTTest < Sablon::TestCase
32
33
  assert_equal '<Root: [<Paragraph{Paragraph}: [<Run{}: Lorem >, <Run{i}: ipsum dolor sit amet>]>]>', ast.inspect
33
34
  end
34
35
 
36
+ def test_a
37
+ input = '<p>Lorem <a href="http://www.google.com">google</a></p>'
38
+ ast = @converter.processed_ast(input)
39
+ assert_equal '<Root: [<Paragraph{Paragraph}: [<Run{}: Lorem >, <Hyperlink{target:http://www.google.com}: [<Run{rStyle=Hyperlink}: google>]>]>]>', ast.inspect
40
+ end
41
+
35
42
  def test_br_in_strong
36
43
  input = '<div><strong>Lorem<br />ipsum<br />dolor</strong></div>'
37
44
  par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
@@ -103,6 +110,34 @@ class HTMLConverterASTTest < Sablon::TestCase
103
110
  assert_equal %w[0 1 2 1 0 1 2], get_numpr_prop_from_ast(ast, :ilvl)
104
111
  end
105
112
 
113
+ def test_table_tag
114
+ input='<table></table>'
115
+ ast = @converter.processed_ast(input)
116
+ assert_equal '<Root: [<Table{}: []>]>', ast.inspect
117
+ end
118
+
119
+ def test_table_with_table_row
120
+ input='<table><tr></tr></table>'
121
+ ast = @converter.processed_ast(input)
122
+ assert_equal '<Root: [<Table{}: [<TableRow{}: []>]>]>', ast.inspect
123
+ end
124
+
125
+ def test_table_with_table_row_and_table_cell
126
+ input='<table><tr><td>Content</td></tr></table>'
127
+ ast = @converter.processed_ast(input)
128
+ assert_equal '<Root: [<Table{}: [<TableRow{}: [<TableCell{}: [<Paragraph{Paragraph}: [<Run{}: Content>]>]>]>]>]>', ast.inspect
129
+ end
130
+
131
+ def test_table_with_table_row_and_table_cell_and_caption
132
+ input='<table><caption>Table Title</caption><tr><td>Content</td></tr></table>'
133
+ ast = @converter.processed_ast(input)
134
+ assert_equal '<Root: [<Table{}: <Paragraph{Caption}: [<Run{}: Table Title>]>, [<TableRow{}: [<TableCell{}: [<Paragraph{Paragraph}: [<Run{}: Content>]>]>]>]>]>', ast.inspect
135
+ #
136
+ input='<table><caption style="caption-side: bottom">Table Title</caption><tr><td>Content</td></tr></table>'
137
+ ast = @converter.processed_ast(input)
138
+ assert_equal '<Root: [<Table{}: [<TableRow{}: [<TableCell{}: [<Paragraph{Paragraph}: [<Run{}: Content>]>]>]>], <Paragraph{Caption}: [<Run{}: Table Title>]>>]>', ast.inspect
139
+ end
140
+
106
141
  private
107
142
 
108
143
  # returns the numid attribute from paragraphs
@@ -0,0 +1,535 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "test_helper"
3
+
4
+ class HTMLConverterStyleTest < Sablon::TestCase
5
+ def setup
6
+ super
7
+ @env = Sablon::Environment.new(nil)
8
+ @converter = Sablon::HTMLConverter.new
9
+ end
10
+
11
+ # testing direct CSS style -> WordML conversion for paragraphs
12
+
13
+ def test_paragraph_with_background_color
14
+ input = '<p style="background-color: #123456"></p>'
15
+ expected_output = para_with_ppr('<w:shd w:val="clear" w:fill="123456" />')
16
+ assert_equal normalize_wordml(expected_output), process(input)
17
+ end
18
+
19
+ def test_paragraph_with_borders
20
+ # Basic single line black border
21
+ input = '<p style="border: 1px"></p>'
22
+ ppr = <<-DOCX.strip
23
+ <w:pBdr>
24
+ <w:top w:sz="2" w:val="single" w:color="000000" />
25
+ <w:bottom w:sz="2" w:val="single" w:color="000000" />
26
+ <w:left w:sz="2" w:val="single" w:color="000000" />
27
+ <w:right w:sz="2" w:val="single" w:color="000000" />
28
+ </w:pBdr>
29
+ DOCX
30
+ expected_output = para_with_ppr(ppr)
31
+ assert_equal normalize_wordml(expected_output), process(input)
32
+ # border with a line style
33
+ input = '<p style="border: 1px wavy"></p>'
34
+ ppr = <<-DOCX.strip
35
+ <w:pBdr>
36
+ <w:top w:sz="2" w:val="wavy" w:color="000000" />
37
+ <w:bottom w:sz="2" w:val="wavy" w:color="000000" />
38
+ <w:left w:sz="2" w:val="wavy" w:color="000000" />
39
+ <w:right w:sz="2" w:val="wavy" w:color="000000" />
40
+ </w:pBdr>
41
+ DOCX
42
+ expected_output = para_with_ppr(ppr)
43
+ assert_equal normalize_wordml(expected_output), process(input)
44
+ # border with line style and color
45
+ input = '<p style="border: 1px wavy #123456"></p>'
46
+ ppr = <<-DOCX.strip
47
+ <w:pBdr>
48
+ <w:top w:sz="2" w:val="wavy" w:color="123456" />
49
+ <w:bottom w:sz="2" w:val="wavy" w:color="123456" />
50
+ <w:left w:sz="2" w:val="wavy" w:color="123456" />
51
+ <w:right w:sz="2" w:val="wavy" w:color="123456" />
52
+ </w:pBdr>
53
+ DOCX
54
+ expected_output = para_with_ppr(ppr)
55
+ assert_equal normalize_wordml(expected_output), process(input)
56
+ end
57
+
58
+ def test_paragraph_with_text_align
59
+ input = '<p style="text-align: both"></p>'
60
+ expected_output = para_with_ppr('<w:jc w:val="both" />')
61
+ assert_equal normalize_wordml(expected_output), process(input)
62
+ end
63
+
64
+ def test_paragraph_with_vertical_align
65
+ input = '<p style="vertical-align: baseline"></p>'
66
+ expected_output = para_with_ppr('<w:textAlignment w:val="baseline" />')
67
+ assert_equal normalize_wordml(expected_output), process(input)
68
+ end
69
+
70
+ def test_paragraph_with_unsupported_property
71
+ input = '<p style="unsupported: true"></p>'
72
+ expected_output = para_with_ppr('')
73
+ assert_equal normalize_wordml(expected_output), process(input)
74
+ end
75
+
76
+ # test that styles defined on the <a> tag are passed down to runs
77
+ def test_hyperlink_with_font_style
78
+ uid_generator = UIDTestGenerator.new
79
+ SecureRandom.stub(:uuid, uid_generator.method(:new_uid)) do |secure_random_instance|
80
+ uid_generator.reset
81
+ input = '<p><a href="http://www.google.com" style="font-style: italic">Google</a></p>'
82
+ expected_output = hyperlink_with_rpr('<w:i />', secure_random_instance.uuid)
83
+ uid_generator.reset
84
+ assert_equal normalize_wordml(expected_output), process(input)
85
+ end
86
+ end
87
+
88
+ def test_run_with_background_color
89
+ input = '<p><span style="background-color: #123456">test</span></p>'
90
+ expected_output = run_with_rpr('<w:shd w:val="clear" w:fill="123456" />')
91
+ assert_equal normalize_wordml(expected_output), process(input)
92
+ end
93
+
94
+ def test_run_with_color
95
+ input = '<p><span style="color: #123456">test</span></p>'
96
+ expected_output = run_with_rpr('<w:color w:val="123456" />')
97
+ assert_equal normalize_wordml(expected_output), process(input)
98
+ end
99
+
100
+ def test_run_with_font_size
101
+ input = '<p><span style="font-size: 20">test</span></p>'
102
+ expected_output = run_with_rpr('<w:sz w:val="40" />')
103
+ assert_equal normalize_wordml(expected_output), process(input)
104
+
105
+ # test that non-numeric are ignored
106
+ input = '<p><span style="font-size: 20pts">test</span></p>'
107
+ assert_equal normalize_wordml(expected_output), process(input)
108
+
109
+ # test that floats round up
110
+ input = '<p><span style="font-size: 19.1pts">test</span></p>'
111
+ assert_equal normalize_wordml(expected_output), process(input)
112
+ end
113
+
114
+ def test_run_with_font_style
115
+ input = '<p><span style="font-style: bold">test</span></p>'
116
+ expected_output = run_with_rpr('<w:b />')
117
+ assert_equal normalize_wordml(expected_output), process(input)
118
+
119
+ # test italics
120
+ input = '<p><span style="font-style: italic">test</span></p>'
121
+ expected_output = run_with_rpr('<w:i />')
122
+ assert_equal normalize_wordml(expected_output), process(input)
123
+ end
124
+
125
+ def test_run_with_font_wieght
126
+ input = '<p><span style="font-weight: bold">test</span></p>'
127
+ expected_output = run_with_rpr('<w:b />')
128
+ assert_equal normalize_wordml(expected_output), process(input)
129
+ end
130
+
131
+ def test_run_with_text_decoration
132
+ # testing underline configurations
133
+ input = '<p><span style="text-decoration: underline">test</span></p>'
134
+ expected_output = run_with_rpr('<w:u w:val="single" />')
135
+ assert_equal normalize_wordml(expected_output), process(input)
136
+
137
+ input = '<p><span style="text-decoration: underline dash">test</span></p>'
138
+ expected_output = run_with_rpr('<w:u w:val="dash" w:color="auto" />')
139
+ assert_equal normalize_wordml(expected_output), process(input)
140
+
141
+ input = '<p><span style="text-decoration: underline dash #123456">test</span></p>'
142
+ expected_output = run_with_rpr('<w:u w:val="dash" w:color="123456" />')
143
+ assert_equal normalize_wordml(expected_output), process(input)
144
+
145
+ # testing line-through
146
+ input = '<p><span style="text-decoration: line-through">test</span></p>'
147
+ expected_output = run_with_rpr('<w:strike w:val="true" />')
148
+ assert_equal normalize_wordml(expected_output), process(input)
149
+
150
+ # testing that unsupported values are passed through as a toggle
151
+ input = '<p><span style="text-decoration: strike">test</span></p>'
152
+ expected_output = run_with_rpr('<w:strike w:val="true" />')
153
+ assert_equal normalize_wordml(expected_output), process(input)
154
+
155
+ input = '<p><span style="text-decoration: emboss">test</span></p>'
156
+ expected_output = run_with_rpr('<w:emboss w:val="true" />')
157
+ assert_equal normalize_wordml(expected_output), process(input)
158
+ end
159
+
160
+ def test_run_with_vertical_align
161
+ input = '<p><span style="vertical-align: subscript">test</span></p>'
162
+ expected_output = run_with_rpr('<w:vertAlign w:val="subscript" />')
163
+ assert_equal normalize_wordml(expected_output), process(input)
164
+
165
+ input = '<p><span style="vertical-align: superscript">test</span></p>'
166
+ expected_output = run_with_rpr('<w:vertAlign w:val="superscript" />')
167
+ assert_equal normalize_wordml(expected_output), process(input)
168
+ end
169
+
170
+ def test_run_with_unsupported_property
171
+ input = '<p><span style="unsupported: true">test</span></p>'
172
+ expected_output = '<w:p><w:pPr><w:pStyle w:val="Paragraph" /></w:pPr><w:r><w:t xml:space="preserve">test</w:t></w:r></w:p>'
173
+ assert_equal normalize_wordml(expected_output), process(input)
174
+ end
175
+
176
+ # tests with nested runs and styles
177
+
178
+ def test_paragraph_props_passed_to_runs
179
+ input = '<p style="color: #123456"><b>Lorem</b><span>ipsum</span></p>'
180
+ expected_output = <<-DOCX.strip
181
+ <w:p>
182
+ <w:pPr>
183
+ <w:pStyle w:val="Paragraph" />
184
+ </w:pPr>
185
+ <w:r>
186
+ <w:rPr>
187
+ <w:color w:val="123456" />
188
+ <w:b />
189
+ </w:rPr>
190
+ <w:t xml:space="preserve">Lorem</w:t>
191
+ </w:r>
192
+ <w:r>
193
+ <w:rPr>
194
+ <w:color w:val="123456" />
195
+ </w:rPr>
196
+ <w:t xml:space="preserve">ipsum</w:t>
197
+ </w:r>
198
+ </w:p>
199
+ DOCX
200
+ assert_equal normalize_wordml(expected_output), process(input)
201
+ end
202
+
203
+ def test_run_prop_override_paragraph_prop
204
+ input = '<p style="text-align: center; color: #FF0000">Lorem<span style="color: blue;">ipsum</span></p>'
205
+ expected_output = <<-DOCX.strip
206
+ <w:p>
207
+ <w:pPr>
208
+ <w:pStyle w:val="Paragraph" />
209
+ <w:jc w:val="center" />
210
+ </w:pPr>
211
+ <w:r>
212
+ <w:rPr>
213
+ <w:color w:val="FF0000" />
214
+ </w:rPr>
215
+ <w:t xml:space="preserve">Lorem</w:t>
216
+ </w:r>
217
+ <w:r>
218
+ <w:rPr>
219
+ <w:color w:val="blue" />
220
+ </w:rPr>
221
+ <w:t xml:space="preserve">ipsum</w:t>
222
+ </w:r>
223
+ </w:p>
224
+ DOCX
225
+ assert_equal normalize_wordml(expected_output), process(input)
226
+ end
227
+
228
+ def test_inline_style_overrides_tag_style
229
+ # Note: a toggle property can not be removed once it becomes a symbol
230
+ # unless there is a specific CSS style that will set it to false. This
231
+ # is because CSS styles can only override parent properties not remove them.
232
+ input = '<p><u style="text-decoration: underline wavyDouble">test</u></p>'
233
+ expected_output = run_with_rpr('<w:u w:val="wavyDouble" w:color="auto" />')
234
+ assert_equal normalize_wordml(expected_output), process(input)
235
+ end
236
+
237
+ def test_conversion_of_a_registered_tag_without_ast_class
238
+ # This registers a new tag with the configuration object and then trys
239
+ # to convert it
240
+ Sablon.configure do |config|
241
+ config.register_html_tag(:bgcyan, :inline, properties: { 'highlight' => { val: 'cyan' } })
242
+ end
243
+ #
244
+ input = '<p><bgcyan>test</bgcyan></p>'
245
+ expected_output = run_with_rpr('<w:highlight w:val="cyan" />')
246
+ assert_equal normalize_wordml(expected_output), process(input)
247
+
248
+ # remove the tag to avoid any accidental side effects
249
+ Sablon.configure do |config|
250
+ config.remove_html_tag(:bgcyan)
251
+ end
252
+ end
253
+
254
+ def test_conversion_of_a_registered_tag_with_ast_class
255
+ Sablon.configure do |config|
256
+ # create the AST class and then pass it onto the register tag method
257
+ ast_class = Class.new(Sablon::HTMLConverter::Node) do
258
+ def self.name
259
+ 'TestInstr'
260
+ end
261
+
262
+ def initialize(_env, node, _properties)
263
+ @content = node.text
264
+ end
265
+
266
+ def inspect
267
+ @content
268
+ end
269
+
270
+ def to_docx
271
+ "<w:instrText xml:space=\"preserve\"> #{@content} </w:instrText>"
272
+ end
273
+ end
274
+ #
275
+ config.register_html_tag(:test_instr, :inline, ast_class: ast_class)
276
+ end
277
+ #
278
+ input = '<p><test_instr>test</test_instr></p>'
279
+ expected_output = <<-DOCX.strip
280
+ <w:p>
281
+ <w:pPr>
282
+ <w:pStyle w:val="Paragraph" />
283
+ </w:pPr>
284
+ <w:instrText xml:space="preserve"> test </w:instrText>
285
+ </w:p>
286
+ DOCX
287
+ assert_equal normalize_wordml(expected_output), process(input)
288
+
289
+ # remove the tag to avoid any accidental side effects
290
+ Sablon.configure do |config|
291
+ config.remove_html_tag(:test_instr)
292
+ end
293
+ end
294
+
295
+ def test_conversion_of_registered_style_attribute
296
+ Sablon.configure do |config|
297
+ converter = ->(v) { return :highlight, v }
298
+ config.register_style_converter(:run, 'test-highlight', converter)
299
+ end
300
+ #
301
+ input = '<p><span style="test-highlight: green">test</span></p>'
302
+ expected_output = run_with_rpr('<w:highlight w:val="green" />')
303
+ assert_equal normalize_wordml(expected_output), process(input)
304
+ #
305
+ Sablon.configure do |config|
306
+ config.remove_style_converter(:run, 'test-highlight')
307
+ end
308
+ end
309
+
310
+ def test_table_border_conversion
311
+ input = '<table style="border: 1px dotted #eaf"><tr><td></td></tr></table>'
312
+ props = <<-DOCX.strip
313
+ <w:tblBorders>
314
+ <w:top w:sz="2" w:val="dotted" w:color="eaf" />
315
+ <w:start w:sz="2" w:val="dotted" w:color="eaf" />
316
+ <w:bottom w:sz="2" w:val="dotted" w:color="eaf" />
317
+ <w:end w:sz="2" w:val="dotted" w:color="eaf" />
318
+ <w:insideH w:sz="2" w:val="dotted" w:color="eaf" />
319
+ <w:insideV w:sz="2" w:val="dotted" w:color="eaf" />
320
+ </w:tblBorders>
321
+ DOCX
322
+ expected_output = table_with_props(props)
323
+ assert_equal normalize_wordml(expected_output), process(input)
324
+ end
325
+
326
+ def test_table_margin_conversion
327
+ # test with single value
328
+ input = '<table style="margin: 2"><tr><td></td></tr></table>'
329
+ props = <<-DOCX.strip
330
+ <w:tblCellMar>
331
+ <w:top w:w="4" w:type="dxa" />
332
+ <w:end w:w="4" w:type="dxa" />
333
+ <w:bottom w:w="4" w:type="dxa" />
334
+ <w:start w:w="4" w:type="dxa" />
335
+ </w:tblCellMar>
336
+ DOCX
337
+ expected_output = table_with_props(props)
338
+ assert_equal normalize_wordml(expected_output), process(input)
339
+
340
+ # test with two values
341
+ input = '<table style="margin: 2 4"><tr><td></td></tr></table>'
342
+ props = <<-DOCX.strip
343
+ <w:tblCellMar>
344
+ <w:top w:w="4" w:type="dxa" />
345
+ <w:end w:w="8" w:type="dxa" />
346
+ <w:bottom w:w="4" w:type="dxa" />
347
+ <w:start w:w="8" w:type="dxa" />
348
+ </w:tblCellMar>
349
+ DOCX
350
+ expected_output = table_with_props(props)
351
+ assert_equal normalize_wordml(expected_output), process(input)
352
+
353
+ # test with three values
354
+ input = '<table style="margin: 2 4 8"><tr><td></td></tr></table>'
355
+ props = <<-DOCX.strip
356
+ <w:tblCellMar>
357
+ <w:top w:w="4" w:type="dxa" />
358
+ <w:end w:w="8" w:type="dxa" />
359
+ <w:bottom w:w="16" w:type="dxa" />
360
+ <w:start w:w="8" w:type="dxa" />
361
+ </w:tblCellMar>
362
+ DOCX
363
+ expected_output = table_with_props(props)
364
+ assert_equal normalize_wordml(expected_output), process(input)
365
+
366
+ # test with four values
367
+ input = '<table style="margin: 2 4 8 16"><tr><td></td></tr></table>'
368
+ props = <<-DOCX.strip
369
+ <w:tblCellMar>
370
+ <w:top w:w="4" w:type="dxa" />
371
+ <w:end w:w="8" w:type="dxa" />
372
+ <w:bottom w:w="16" w:type="dxa" />
373
+ <w:start w:w="32" w:type="dxa" />
374
+ </w:tblCellMar>
375
+ DOCX
376
+ expected_output = table_with_props(props)
377
+ assert_equal normalize_wordml(expected_output), process(input)
378
+ end
379
+
380
+ def test_table_cellspacing_conversion
381
+ input = '<table style="cellspacing: 1"><tr><td></td></tr></table>'
382
+ expected_output = table_with_props('<w:tblCellSpacing w:w="2" w:type="dxa" />')
383
+ assert_equal normalize_wordml(expected_output), process(input)
384
+ end
385
+
386
+ def test_table_width_conversion
387
+ input = '<table style="width: 1"><tr><td></td></tr></table>'
388
+ expected_output = table_with_props('<w:tblW w:w="2" w:type="dxa" />')
389
+ assert_equal normalize_wordml(expected_output), process(input)
390
+ end
391
+
392
+ def test_table_cell_borders_conversion
393
+ input = '<table><tr><td style="border: 1px dotted #eaf"></td></tr></table>'
394
+ props = <<-DOCX.strip
395
+ <w:tcBorders>
396
+ <w:top w:sz="2" w:val="dotted" w:color="eaf" />
397
+ <w:start w:sz="2" w:val="dotted" w:color="eaf" />
398
+ <w:bottom w:sz="2" w:val="dotted" w:color="eaf" />
399
+ <w:end w:sz="2" w:val="dotted" w:color="eaf" />
400
+ <w:insideH w:sz="2" w:val="dotted" w:color="eaf" />
401
+ <w:insideV w:sz="2" w:val="dotted" w:color="eaf" />
402
+ </w:tcBorders>
403
+ DOCX
404
+ expected_output = table_with_props('', '', props)
405
+ assert_equal normalize_wordml(expected_output), process(input)
406
+ end
407
+
408
+ def test_table_cell_colspan_conversion
409
+ input = '<table><tr><td style="colspan: 2"></td></tr></table>'
410
+ expected_output = table_with_props('', '', '<w:gridSpan w:val="2" />')
411
+ assert_equal normalize_wordml(expected_output), process(input)
412
+ end
413
+
414
+ def test_table_cell_margin_conversion
415
+ # test with four values
416
+ input = '<table><tr><td style="margin: 2 4 8 16"></td></tr></table>'
417
+ props = <<-DOCX.strip
418
+ <w:tcMar>
419
+ <w:top w:w="4" w:type="dxa" />
420
+ <w:end w:w="8" w:type="dxa" />
421
+ <w:bottom w:w="16" w:type="dxa" />
422
+ <w:start w:w="32" w:type="dxa" />
423
+ </w:tcMar>
424
+ DOCX
425
+ expected_output = table_with_props('', '', props)
426
+ assert_equal normalize_wordml(expected_output), process(input)
427
+ end
428
+
429
+ def test_table_cell_rowspan_conversion
430
+ input = '<table><tr><td style="rowspan: start"></td></tr></table>'
431
+ expected_output = table_with_props('', '', '<w:vMerge w:val="restart" />')
432
+ assert_equal normalize_wordml(expected_output), process(input)
433
+ #
434
+ input = '<table><tr><td style="rowspan: continue"></td></tr></table>'
435
+ expected_output = table_with_props('', '', '<w:vMerge w:val="continue" />')
436
+ assert_equal normalize_wordml(expected_output), process(input)
437
+ #
438
+ input = '<table><tr><td style="rowspan: end"></td></tr></table>'
439
+ expected_output = table_with_props('', '', '<w:vMerge />')
440
+ assert_equal normalize_wordml(expected_output), process(input)
441
+ end
442
+
443
+ def test_table_cell_vertical_align_conversion
444
+ input = '<table><tr><td style="vertical-align: top"></td></tr></table>'
445
+ expected_output = table_with_props('', '', '<w:vAlign w:val="top" />')
446
+ assert_equal normalize_wordml(expected_output), process(input)
447
+ end
448
+
449
+ def test_table_cell_white_space_conversion
450
+ input = '<table><tr><td style="white-space: nowrap"></td></tr></table>'
451
+ expected_output = table_with_props('', '', '<w:noWrap />')
452
+ assert_equal normalize_wordml(expected_output), process(input)
453
+ #
454
+ input = '<table><tr><td style="white-space: fit"></td></tr></table>'
455
+ expected_output = table_with_props('', '', '<w:tcFitText w:val="true" />')
456
+ assert_equal normalize_wordml(expected_output), process(input)
457
+ end
458
+
459
+ def test_table_cell_width_conversion
460
+ input = '<table><tr><td style="width: 100"></td></tr></table>'
461
+ expected_output = table_with_props('', '', '<w:tcW w:w="200" w:type="dxa" />')
462
+ assert_equal normalize_wordml(expected_output), process(input)
463
+ end
464
+
465
+ private
466
+
467
+ def process(input)
468
+ @converter.process(input, @env)
469
+ end
470
+
471
+ def table_with_props(tbl_pr_str, tr_pr_str = '', tc_pr_str = '')
472
+ tbl_str = <<-DOCX.strip
473
+ <w:tbl>
474
+ %s
475
+ <w:tr>
476
+ %s
477
+ <w:tc>
478
+ %s
479
+ <w:p><w:pPr><w:pStyle w:val="Paragraph" /></w:pPr></w:p>
480
+ </w:tc>
481
+ </w:tr>
482
+ </w:tbl>
483
+ DOCX
484
+ tbl_pr_str = "<w:tblPr>#{tbl_pr_str}</w:tblPr>" unless tbl_pr_str == ''
485
+ tr_pr_str = "<w:trPr>#{tr_pr_str}</w:trPr>" unless tr_pr_str == ''
486
+ tc_pr_str = "<w:tcPr>#{tc_pr_str}</w:tcPr>" unless tc_pr_str == ''
487
+ format(tbl_str, tbl_pr_str, tr_pr_str, tc_pr_str)
488
+ end
489
+
490
+ def para_with_ppr(ppr_str)
491
+ para_str = '<w:p><w:pPr><w:pStyle w:val="Paragraph" />%s</w:pPr></w:p>'
492
+ format(para_str, ppr_str)
493
+ end
494
+
495
+ def run_with_rpr(rpr_str)
496
+ para_str = <<-DOCX.strip
497
+ <w:p>
498
+ <w:pPr>
499
+ <w:pStyle w:val="Paragraph" />
500
+ </w:pPr>
501
+ <w:r>
502
+ <w:rPr>
503
+ %s
504
+ </w:rPr>
505
+ <w:t xml:space="preserve">test</w:t>
506
+ </w:r>
507
+ </w:p>
508
+ DOCX
509
+ format(para_str, rpr_str)
510
+ end
511
+
512
+ def hyperlink_with_rpr(rpr_str, id)
513
+ para_str = <<-DOCX.strip
514
+ <w:p>
515
+ <w:pPr>
516
+ <w:pStyle w:val="Paragraph" />
517
+ </w:pPr>
518
+ <w:hyperlink r:id="rId#{id}">
519
+ <w:r>
520
+ <w:rPr>
521
+ <w:rStyle w:val="Hyperlink" />
522
+ %s
523
+ </w:rPr>
524
+ <w:t xml:space="preserve">Google</w:t>
525
+ </w:r>
526
+ </w:hyperlink>
527
+ </w:p>
528
+ DOCX
529
+ format(para_str, rpr_str)
530
+ end
531
+
532
+ def normalize_wordml(wordml)
533
+ wordml.gsub(/^\s+/, '').tr("\n", '')
534
+ end
535
+ end