prosereflect 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +23 -4
- data/README.adoc +193 -12
- data/lib/prosereflect/attribute/base.rb +34 -0
- data/lib/prosereflect/attribute/bold.rb +20 -0
- data/lib/prosereflect/attribute/href.rb +24 -0
- data/lib/prosereflect/attribute/id.rb +24 -0
- data/lib/prosereflect/attribute.rb +13 -0
- data/lib/prosereflect/blockquote.rb +85 -0
- data/lib/prosereflect/bullet_list.rb +83 -0
- data/lib/prosereflect/code_block.rb +135 -0
- data/lib/prosereflect/code_block_wrapper.rb +66 -0
- data/lib/prosereflect/document.rb +99 -24
- data/lib/prosereflect/hard_break.rb +11 -9
- data/lib/prosereflect/heading.rb +64 -0
- data/lib/prosereflect/horizontal_rule.rb +70 -0
- data/lib/prosereflect/image.rb +126 -0
- data/lib/prosereflect/input/html.rb +505 -0
- data/lib/prosereflect/list_item.rb +65 -0
- data/lib/prosereflect/mark/base.rb +49 -0
- data/lib/prosereflect/mark/bold.rb +15 -0
- data/lib/prosereflect/mark/code.rb +14 -0
- data/lib/prosereflect/mark/italic.rb +15 -0
- data/lib/prosereflect/mark/link.rb +18 -0
- data/lib/prosereflect/mark/strike.rb +15 -0
- data/lib/prosereflect/mark/subscript.rb +15 -0
- data/lib/prosereflect/mark/superscript.rb +15 -0
- data/lib/prosereflect/mark/underline.rb +15 -0
- data/lib/prosereflect/mark.rb +11 -0
- data/lib/prosereflect/node.rb +181 -32
- data/lib/prosereflect/ordered_list.rb +85 -0
- data/lib/prosereflect/output/html.rb +374 -0
- data/lib/prosereflect/paragraph.rb +26 -15
- data/lib/prosereflect/parser.rb +111 -24
- data/lib/prosereflect/table.rb +40 -9
- data/lib/prosereflect/table_cell.rb +33 -8
- data/lib/prosereflect/table_header.rb +92 -0
- data/lib/prosereflect/table_row.rb +31 -8
- data/lib/prosereflect/text.rb +13 -17
- data/lib/prosereflect/user.rb +63 -0
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +6 -0
- data/prosereflect.gemspec +1 -0
- data/spec/prosereflect/document_spec.rb +436 -36
- data/spec/prosereflect/hard_break_spec.rb +218 -22
- data/spec/prosereflect/input/html_spec.rb +797 -0
- data/spec/prosereflect/node_spec.rb +258 -89
- data/spec/prosereflect/output/html_spec.rb +369 -0
- data/spec/prosereflect/paragraph_spec.rb +424 -49
- data/spec/prosereflect/parser_spec.rb +304 -91
- data/spec/prosereflect/table_cell_spec.rb +268 -57
- data/spec/prosereflect/table_row_spec.rb +210 -40
- data/spec/prosereflect/table_spec.rb +392 -61
- data/spec/prosereflect/text_spec.rb +206 -48
- data/spec/prosereflect/user_spec.rb +73 -0
- data/spec/prosereflect_spec.rb +5 -0
- data/spec/support/shared_examples.rb +44 -15
- metadata +47 -3
- data/debug_loading.rb +0 -34
@@ -0,0 +1,369 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Prosereflect::Output::Html do
|
6
|
+
describe '.convert' do
|
7
|
+
it 'converts a simple document to HTML' do
|
8
|
+
document = Prosereflect::Document.new
|
9
|
+
document.add_paragraph('This is a test paragraph.')
|
10
|
+
|
11
|
+
html = described_class.convert(document)
|
12
|
+
expect(html).to eq('<p>This is a test paragraph.</p>')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'renders basic styled text correctly' do
|
16
|
+
document = Prosereflect::Document.new
|
17
|
+
paragraph = document.add_paragraph
|
18
|
+
paragraph.add_text('This is ')
|
19
|
+
|
20
|
+
bold_text = Prosereflect::Text.new(text: 'bold')
|
21
|
+
bold_text.marks = [Prosereflect::Mark::Bold.new]
|
22
|
+
paragraph.add_child(bold_text)
|
23
|
+
|
24
|
+
paragraph.add_text(' and ')
|
25
|
+
|
26
|
+
italic_text = Prosereflect::Text.new(text: 'italic')
|
27
|
+
italic_text.marks = [Prosereflect::Mark::Italic.new]
|
28
|
+
paragraph.add_child(italic_text)
|
29
|
+
|
30
|
+
paragraph.add_text(' text.')
|
31
|
+
|
32
|
+
html = described_class.convert(document)
|
33
|
+
expect(html).to eq('<p>This is <strong>bold</strong> and <em>italic</em> text.</p>')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'renders headings with mixed content correctly' do
|
37
|
+
document = Prosereflect::Document.new
|
38
|
+
heading = Prosereflect::Heading.new
|
39
|
+
heading.level = 1
|
40
|
+
heading.add_text('Title with ')
|
41
|
+
|
42
|
+
bold_text = Prosereflect::Text.new(text: 'bold')
|
43
|
+
bold_text.marks = [Prosereflect::Mark::Bold.new]
|
44
|
+
heading.add_child(bold_text)
|
45
|
+
|
46
|
+
heading.add_text(' and ')
|
47
|
+
|
48
|
+
link_text = Prosereflect::Text.new(text: 'link')
|
49
|
+
link_mark = Prosereflect::Mark::Link.new
|
50
|
+
link_mark.attrs = { 'href' => 'https://example.com' }
|
51
|
+
link_text.marks = [link_mark]
|
52
|
+
heading.add_child(link_text)
|
53
|
+
|
54
|
+
document.add_child(heading)
|
55
|
+
|
56
|
+
html = described_class.convert(document)
|
57
|
+
expect(html).to eq('<h1>Title with <strong>bold</strong> and <a href="https://example.com">link</a></h1>')
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'renders lists with nested content correctly' do
|
61
|
+
document = Prosereflect::Document.new
|
62
|
+
list = Prosereflect::BulletList.new
|
63
|
+
|
64
|
+
# First item with emphasis
|
65
|
+
item1 = Prosereflect::ListItem.new
|
66
|
+
para1 = item1.add_paragraph
|
67
|
+
para1.add_text('First item with ')
|
68
|
+
em_text = Prosereflect::Text.new(text: 'emphasis')
|
69
|
+
em_text.marks = [Prosereflect::Mark::Italic.new]
|
70
|
+
para1.add_child(em_text)
|
71
|
+
list.add_child(item1)
|
72
|
+
|
73
|
+
# Second item with code
|
74
|
+
item2 = Prosereflect::ListItem.new
|
75
|
+
para2 = item2.add_paragraph
|
76
|
+
para2.add_text('Second item with ')
|
77
|
+
code_text = Prosereflect::Text.new(text: 'code')
|
78
|
+
code_text.marks = [Prosereflect::Mark::Code.new]
|
79
|
+
para2.add_child(code_text)
|
80
|
+
list.add_child(item2)
|
81
|
+
|
82
|
+
document.add_child(list)
|
83
|
+
|
84
|
+
html = described_class.convert(document)
|
85
|
+
expect(html).to eq('<ul><li><p>First item with <em>emphasis</em></p></li><li><p>Second item with <code>code</code></p></li></ul>')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'renders blockquotes with citations correctly' do
|
89
|
+
document = Prosereflect::Document.new
|
90
|
+
quote = Prosereflect::Blockquote.new
|
91
|
+
quote.citation = 'https://example.com'
|
92
|
+
|
93
|
+
para = quote.add_paragraph('A quote with ')
|
94
|
+
bold_text = Prosereflect::Text.new(text: 'bold')
|
95
|
+
bold_text.marks = [Prosereflect::Mark::Bold.new]
|
96
|
+
para.add_child(bold_text)
|
97
|
+
para.add_text(' text')
|
98
|
+
|
99
|
+
document.add_child(quote)
|
100
|
+
|
101
|
+
html = described_class.convert(document)
|
102
|
+
expect(html).to eq('<blockquote cite="https://example.com"><p>A quote with <strong>bold</strong> text</p></blockquote>')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'renders code blocks with language correctly' do
|
106
|
+
document = Prosereflect::Document.new
|
107
|
+
wrapper = Prosereflect::CodeBlockWrapper.new
|
108
|
+
|
109
|
+
code_block = wrapper.add_code_block
|
110
|
+
code_block.language = 'ruby'
|
111
|
+
code_block.content = "def example\n puts \"Hello\"\nend"
|
112
|
+
|
113
|
+
document.add_child(wrapper)
|
114
|
+
|
115
|
+
expected = <<~HTML.strip
|
116
|
+
<pre><code class="language-ruby">def example
|
117
|
+
puts "Hello"
|
118
|
+
end</code></pre>
|
119
|
+
HTML
|
120
|
+
|
121
|
+
html = described_class.convert(document)
|
122
|
+
expect(html).to eq(expected.gsub(/^ {10}/, ' '))
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'renders images with attributes correctly' do
|
126
|
+
document = Prosereflect::Document.new
|
127
|
+
image = Prosereflect::Image.new
|
128
|
+
image.src = 'test.jpg'
|
129
|
+
image.alt = 'Test image'
|
130
|
+
image.title = 'Test title'
|
131
|
+
image.width = 800
|
132
|
+
image.height = 600
|
133
|
+
|
134
|
+
document.add_child(image)
|
135
|
+
|
136
|
+
html = described_class.convert(document)
|
137
|
+
expect(html).to eq('<img src="test.jpg" alt="Test image" title="Test title" width="800" height="600">')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'renders horizontal rules with styles correctly' do
|
141
|
+
document = Prosereflect::Document.new
|
142
|
+
hr = Prosereflect::HorizontalRule.new
|
143
|
+
hr.style = 'dashed'
|
144
|
+
hr.width = '80%'
|
145
|
+
hr.thickness = 2
|
146
|
+
|
147
|
+
document.add_child(hr)
|
148
|
+
|
149
|
+
html = described_class.convert(document)
|
150
|
+
expect(html).to eq('<hr style="border-style: dashed; width: 80%; border-width: 2px">')
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'renders complex nested structures correctly' do
|
154
|
+
document = Prosereflect::Document.new
|
155
|
+
|
156
|
+
# Add heading
|
157
|
+
heading = Prosereflect::Heading.new
|
158
|
+
heading.level = 2
|
159
|
+
heading.add_text('Features')
|
160
|
+
document.add_child(heading)
|
161
|
+
|
162
|
+
# Add list with nested content
|
163
|
+
list = Prosereflect::BulletList.new
|
164
|
+
|
165
|
+
# List item with blockquote
|
166
|
+
item1 = Prosereflect::ListItem.new
|
167
|
+
para1 = item1.add_paragraph
|
168
|
+
para1.add_text('Quote: ')
|
169
|
+
list.add_child(item1)
|
170
|
+
|
171
|
+
quote = Prosereflect::Blockquote.new
|
172
|
+
quote.citation = 'https://example.com'
|
173
|
+
quote.add_paragraph('Nested quote')
|
174
|
+
list.add_child(quote)
|
175
|
+
|
176
|
+
# List item with code
|
177
|
+
item2 = Prosereflect::ListItem.new
|
178
|
+
para2 = item2.add_paragraph
|
179
|
+
para2.add_text('Code: ')
|
180
|
+
list.add_child(item2)
|
181
|
+
|
182
|
+
wrapper = Prosereflect::CodeBlockWrapper.new
|
183
|
+
code_block = wrapper.add_code_block
|
184
|
+
code_block.language = 'ruby'
|
185
|
+
code_block.content = 'puts "Hello"'
|
186
|
+
list.add_child(wrapper)
|
187
|
+
|
188
|
+
document.add_child(list)
|
189
|
+
|
190
|
+
html = described_class.convert(document)
|
191
|
+
expected = '<h2>Features</h2>' \
|
192
|
+
'<ul>' \
|
193
|
+
'<li><p>Quote: </p></li>' \
|
194
|
+
'<li><blockquote cite="https://example.com"><p>Nested quote</p></blockquote></li>' \
|
195
|
+
'<li><p>Code: </p></li>' \
|
196
|
+
'<li><pre><code class="language-ruby">puts "Hello"</code></pre></li>' \
|
197
|
+
'</ul>'
|
198
|
+
expect(html).to eq(expected)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'converts tables to HTML' do
|
202
|
+
document = Prosereflect::Document.new
|
203
|
+
table = document.add_table
|
204
|
+
|
205
|
+
# Add header row
|
206
|
+
table.add_header(['Header 1', 'Header 2'])
|
207
|
+
|
208
|
+
# Add data rows
|
209
|
+
table.add_row(['Row 1, Cell 1', 'Row 1, Cell 2'])
|
210
|
+
table.add_row(['Row 2, Cell 1', 'Row 2, Cell 2'])
|
211
|
+
|
212
|
+
html = described_class.convert(document)
|
213
|
+
expected = '<table>' \
|
214
|
+
'<thead><tr><th>Header 1</th><th>Header 2</th></tr></thead>' \
|
215
|
+
'<tbody>' \
|
216
|
+
'<tr><td>Row 1, Cell 1</td><td>Row 1, Cell 2</td></tr>' \
|
217
|
+
'<tr><td>Row 2, Cell 1</td><td>Row 2, Cell 2</td></tr>' \
|
218
|
+
'</tbody>' \
|
219
|
+
'</table>'
|
220
|
+
expect(html).to eq(expected)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'converts complex table headers' do
|
224
|
+
document = Prosereflect::Document.new
|
225
|
+
table = document.add_table
|
226
|
+
|
227
|
+
# Add header row with complex cells
|
228
|
+
header = table.add_header(['Product'])
|
229
|
+
header.cells.first.scope = 'col'
|
230
|
+
header.cells.first.abbr = 'Prod'
|
231
|
+
header.cells.first.colspan = 2
|
232
|
+
|
233
|
+
html = described_class.convert(document)
|
234
|
+
expected = '<table><thead><tr><th scope="col" abbr="Prod" colspan="2">Product</th></tr></thead><tbody></tbody></table>'
|
235
|
+
expect(html).to eq(expected)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'converts text with all mark types' do
|
239
|
+
document = Prosereflect::Document.new
|
240
|
+
paragraph = document.add_paragraph
|
241
|
+
|
242
|
+
paragraph.add_text('struck', [Prosereflect::Mark::Strike.new])
|
243
|
+
paragraph.add_text(' and ')
|
244
|
+
paragraph.add_text('underlined', [Prosereflect::Mark::Underline.new])
|
245
|
+
paragraph.add_text(' and ')
|
246
|
+
paragraph.add_text('sub', [Prosereflect::Mark::Subscript.new])
|
247
|
+
paragraph.add_text(' and ')
|
248
|
+
paragraph.add_text('super', [Prosereflect::Mark::Superscript.new])
|
249
|
+
|
250
|
+
html = described_class.convert(document)
|
251
|
+
expect(html).to eq('<p><del>struck</del> and <u>underlined</u> and <sub>sub</sub> and <sup>super</sup></p>')
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'converts images with all attributes' do
|
255
|
+
document = Prosereflect::Document.new
|
256
|
+
image = document.add_image('example.jpg', 'Example image')
|
257
|
+
image.title = 'Image tooltip'
|
258
|
+
image.width = 800
|
259
|
+
image.height = 600
|
260
|
+
|
261
|
+
html = described_class.convert(document)
|
262
|
+
expect(html).to eq('<img src="example.jpg" alt="Example image" title="Image tooltip" width="800" height="600">')
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'converts code blocks with wrapper attributes' do
|
266
|
+
document = Prosereflect::Document.new
|
267
|
+
wrapper = document.add_code_block_wrapper
|
268
|
+
wrapper.line_numbers = true
|
269
|
+
|
270
|
+
code_block = wrapper.add_code_block
|
271
|
+
code_block.language = 'ruby'
|
272
|
+
code_block.content = "def example\n puts 'Hello'\nend"
|
273
|
+
|
274
|
+
html = described_class.convert(document)
|
275
|
+
expected = '<pre data-line-numbers="true"><code class="language-ruby">def example
|
276
|
+
puts \'Hello\'
|
277
|
+
end</code></pre>'
|
278
|
+
expect(html).to eq(expected)
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'handles complex nested structures with tables' do
|
282
|
+
document = Prosereflect::Document.new
|
283
|
+
|
284
|
+
# Add a paragraph with styled text
|
285
|
+
document.add_paragraph('Paragraph 1')
|
286
|
+
|
287
|
+
# Add a paragraph with mixed styling
|
288
|
+
p2 = document.add_paragraph
|
289
|
+
p2.add_text('Paragraph ')
|
290
|
+
p2.add_text('2', [Prosereflect::Mark::Bold.new])
|
291
|
+
|
292
|
+
# Add a table
|
293
|
+
table = document.add_table
|
294
|
+
table.add_row(['Cell 1', 'Cell 2'])
|
295
|
+
|
296
|
+
html = described_class.convert(document)
|
297
|
+
expected = '<p>Paragraph 1</p><p>Paragraph <strong>2</strong></p><table><tbody><tr><td>Cell 1</td><td>Cell 2</td></tr></tbody></table>'
|
298
|
+
expect(html).to eq(expected)
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'renders user mentions correctly' do
|
302
|
+
document = Prosereflect::Document.new
|
303
|
+
user = Prosereflect::User.new
|
304
|
+
user.id = '123'
|
305
|
+
document.add_child(user)
|
306
|
+
|
307
|
+
html = described_class.convert(document)
|
308
|
+
expect(html).to eq('<user-mention data-id="123"></user-mention>')
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'renders user mentions in paragraphs' do
|
312
|
+
document = Prosereflect::Document.new
|
313
|
+
paragraph = document.add_paragraph
|
314
|
+
paragraph.add_text('Hello ')
|
315
|
+
|
316
|
+
user = Prosereflect::User.new
|
317
|
+
user.id = '123'
|
318
|
+
paragraph.add_child(user)
|
319
|
+
|
320
|
+
paragraph.add_text('!')
|
321
|
+
|
322
|
+
html = described_class.convert(document)
|
323
|
+
expect(html).to eq('<p>Hello <user-mention data-id="123"></user-mention>!</p>')
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'renders multiple user mentions' do
|
327
|
+
document = Prosereflect::Document.new
|
328
|
+
paragraph = document.add_paragraph
|
329
|
+
paragraph.add_text('Mentioned: ')
|
330
|
+
|
331
|
+
user1 = Prosereflect::User.new
|
332
|
+
user1.id = '123'
|
333
|
+
paragraph.add_child(user1)
|
334
|
+
|
335
|
+
paragraph.add_text(' and ')
|
336
|
+
|
337
|
+
user2 = Prosereflect::User.new
|
338
|
+
user2.id = '456'
|
339
|
+
paragraph.add_child(user2)
|
340
|
+
|
341
|
+
html = described_class.convert(document)
|
342
|
+
expect(html).to eq('<p>Mentioned: <user-mention data-id="123"></user-mention> and <user-mention data-id="456"></user-mention></p>')
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'renders user mentions with other marks' do
|
346
|
+
document = Prosereflect::Document.new
|
347
|
+
paragraph = document.add_paragraph
|
348
|
+
|
349
|
+
bold_text = Prosereflect::Text.new(text: 'Bold')
|
350
|
+
bold_text.marks = [Prosereflect::Mark::Bold.new]
|
351
|
+
paragraph.add_child(bold_text)
|
352
|
+
|
353
|
+
paragraph.add_text(' and ')
|
354
|
+
|
355
|
+
user = Prosereflect::User.new
|
356
|
+
user.id = '123'
|
357
|
+
paragraph.add_child(user)
|
358
|
+
|
359
|
+
paragraph.add_text(' and ')
|
360
|
+
|
361
|
+
italic_text = Prosereflect::Text.new(text: 'italic')
|
362
|
+
italic_text.marks = [Prosereflect::Mark::Italic.new]
|
363
|
+
paragraph.add_child(italic_text)
|
364
|
+
|
365
|
+
html = described_class.convert(document)
|
366
|
+
expect(html).to eq('<p><strong>Bold</strong> and <user-mention data-id="123"></user-mention> and <em>italic</em></p>')
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|