sablon 0.0.22 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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