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
@@ -19,23 +19,415 @@ RSpec.describe Prosereflect::Document do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
describe '.create' do
|
22
|
-
it 'creates
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
it 'creates a simple document with a paragraph' do
|
23
|
+
document = described_class.new
|
24
|
+
document.add_paragraph('This is a test paragraph.')
|
25
|
+
|
26
|
+
expected = {
|
27
|
+
'type' => 'doc',
|
28
|
+
'content' => [{
|
29
|
+
'type' => 'paragraph',
|
30
|
+
'content' => [{
|
31
|
+
'type' => 'text',
|
32
|
+
'text' => 'This is a test paragraph.'
|
33
|
+
}]
|
34
|
+
}]
|
35
|
+
}
|
36
|
+
|
37
|
+
expect(document.to_h).to eq(expected)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'creates a document with multiple paragraphs and styles' do
|
41
|
+
document = described_class.new
|
42
|
+
|
43
|
+
# Add a heading
|
44
|
+
heading = document.add_heading(1)
|
45
|
+
heading.add_text('Document Title')
|
46
|
+
|
47
|
+
# Add paragraphs with different styles
|
48
|
+
para1 = document.add_paragraph
|
49
|
+
para1.add_text('This is a paragraph with ')
|
50
|
+
para1.add_text('bold', [Prosereflect::Mark::Bold.new])
|
51
|
+
para1.add_text(' and ')
|
52
|
+
para1.add_text('italic', [Prosereflect::Mark::Italic.new])
|
53
|
+
para1.add_text(' text.')
|
54
|
+
|
55
|
+
para2 = document.add_paragraph
|
56
|
+
para2.add_text('This paragraph has ')
|
57
|
+
para2.add_text('underlined', [Prosereflect::Mark::Underline.new])
|
58
|
+
para2.add_text(' and ')
|
59
|
+
para2.add_text('struck through', [Prosereflect::Mark::Strike.new])
|
60
|
+
para2.add_text(' text.')
|
61
|
+
|
62
|
+
expected = {
|
63
|
+
'type' => 'doc',
|
64
|
+
'content' => [{
|
65
|
+
'type' => 'heading',
|
66
|
+
'attrs' => { 'level' => 1 },
|
67
|
+
'content' => [{
|
68
|
+
'type' => 'text',
|
69
|
+
'text' => 'Document Title'
|
70
|
+
}]
|
71
|
+
}, {
|
72
|
+
'type' => 'paragraph',
|
73
|
+
'content' => [{
|
74
|
+
'type' => 'text',
|
75
|
+
'text' => 'This is a paragraph with '
|
76
|
+
}, {
|
77
|
+
'type' => 'text',
|
78
|
+
'text' => 'bold',
|
79
|
+
'marks' => [{ 'type' => 'bold' }]
|
80
|
+
}, {
|
81
|
+
'type' => 'text',
|
82
|
+
'text' => ' and '
|
83
|
+
}, {
|
84
|
+
'type' => 'text',
|
85
|
+
'text' => 'italic',
|
86
|
+
'marks' => [{ 'type' => 'italic' }]
|
87
|
+
}, {
|
88
|
+
'type' => 'text',
|
89
|
+
'text' => ' text.'
|
90
|
+
}]
|
91
|
+
}, {
|
92
|
+
'type' => 'paragraph',
|
93
|
+
'content' => [{
|
94
|
+
'type' => 'text',
|
95
|
+
'text' => 'This paragraph has '
|
96
|
+
}, {
|
97
|
+
'type' => 'text',
|
98
|
+
'text' => 'underlined',
|
99
|
+
'marks' => [{ 'type' => 'underline' }]
|
100
|
+
}, {
|
101
|
+
'type' => 'text',
|
102
|
+
'text' => ' and '
|
103
|
+
}, {
|
104
|
+
'type' => 'text',
|
105
|
+
'text' => 'struck through',
|
106
|
+
'marks' => [{ 'type' => 'strike' }]
|
107
|
+
}, {
|
108
|
+
'type' => 'text',
|
109
|
+
'text' => ' text.'
|
110
|
+
}]
|
111
|
+
}]
|
112
|
+
}
|
113
|
+
|
114
|
+
expect(document.to_h).to eq(expected)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'creates a document with tables and lists' do
|
118
|
+
document = described_class.new
|
119
|
+
|
120
|
+
# Add a table
|
121
|
+
table = document.add_table
|
122
|
+
table.add_header(%w[Product Price Quantity])
|
123
|
+
table.add_row(['Widget', '$10.00', '5'])
|
124
|
+
table.add_row(['Gadget', '$15.00', '3'])
|
125
|
+
|
126
|
+
# Add an ordered list
|
127
|
+
list = document.add_ordered_list
|
128
|
+
list.start = 1
|
129
|
+
list.add_item('First item')
|
130
|
+
list.add_item('Second item with ')
|
131
|
+
.add_text('emphasis', [Prosereflect::Mark::Italic.new])
|
132
|
+
|
133
|
+
expected = {
|
134
|
+
'type' => 'doc',
|
135
|
+
'content' => [{
|
136
|
+
'type' => 'table',
|
137
|
+
'content' => [{
|
138
|
+
'type' => 'table_row',
|
139
|
+
'content' => [{
|
140
|
+
'type' => 'table_header',
|
141
|
+
'content' => [{
|
142
|
+
'type' => 'paragraph',
|
143
|
+
'content' => [{
|
144
|
+
'type' => 'text',
|
145
|
+
'text' => 'Product'
|
146
|
+
}]
|
147
|
+
}]
|
148
|
+
}, {
|
149
|
+
'type' => 'table_header',
|
150
|
+
'content' => [{
|
151
|
+
'type' => 'paragraph',
|
152
|
+
'content' => [{
|
153
|
+
'type' => 'text',
|
154
|
+
'text' => 'Price'
|
155
|
+
}]
|
156
|
+
}]
|
157
|
+
}, {
|
158
|
+
'type' => 'table_header',
|
159
|
+
'content' => [{
|
160
|
+
'type' => 'paragraph',
|
161
|
+
'content' => [{
|
162
|
+
'type' => 'text',
|
163
|
+
'text' => 'Quantity'
|
164
|
+
}]
|
165
|
+
}]
|
166
|
+
}]
|
167
|
+
}, {
|
168
|
+
'type' => 'table_row',
|
169
|
+
'content' => [{
|
170
|
+
'type' => 'table_cell',
|
171
|
+
'content' => [{
|
172
|
+
'type' => 'paragraph',
|
173
|
+
'content' => [{
|
174
|
+
'type' => 'text',
|
175
|
+
'text' => 'Widget'
|
176
|
+
}]
|
177
|
+
}]
|
178
|
+
}, {
|
179
|
+
'type' => 'table_cell',
|
180
|
+
'content' => [{
|
181
|
+
'type' => 'paragraph',
|
182
|
+
'content' => [{
|
183
|
+
'type' => 'text',
|
184
|
+
'text' => '$10.00'
|
185
|
+
}]
|
186
|
+
}]
|
187
|
+
}, {
|
188
|
+
'type' => 'table_cell',
|
189
|
+
'content' => [{
|
190
|
+
'type' => 'paragraph',
|
191
|
+
'content' => [{
|
192
|
+
'type' => 'text',
|
193
|
+
'text' => '5'
|
194
|
+
}]
|
195
|
+
}]
|
196
|
+
}]
|
197
|
+
}, {
|
198
|
+
'type' => 'table_row',
|
199
|
+
'content' => [{
|
200
|
+
'type' => 'table_cell',
|
201
|
+
'content' => [{
|
202
|
+
'type' => 'paragraph',
|
203
|
+
'content' => [{
|
204
|
+
'type' => 'text',
|
205
|
+
'text' => 'Gadget'
|
206
|
+
}]
|
207
|
+
}]
|
208
|
+
}, {
|
209
|
+
'type' => 'table_cell',
|
210
|
+
'content' => [{
|
211
|
+
'type' => 'paragraph',
|
212
|
+
'content' => [{
|
213
|
+
'type' => 'text',
|
214
|
+
'text' => '$15.00'
|
215
|
+
}]
|
216
|
+
}]
|
217
|
+
}, {
|
218
|
+
'type' => 'table_cell',
|
219
|
+
'content' => [{
|
220
|
+
'type' => 'paragraph',
|
221
|
+
'content' => [{
|
222
|
+
'type' => 'text',
|
223
|
+
'text' => '3'
|
224
|
+
}]
|
225
|
+
}]
|
226
|
+
}]
|
227
|
+
}]
|
228
|
+
}, {
|
229
|
+
'type' => 'ordered_list',
|
230
|
+
'attrs' => { 'start' => 1 },
|
231
|
+
'content' => [{
|
232
|
+
'type' => 'list_item',
|
233
|
+
'content' => [{
|
234
|
+
'type' => 'paragraph',
|
235
|
+
'content' => [{
|
236
|
+
'type' => 'text',
|
237
|
+
'text' => 'First item'
|
238
|
+
}]
|
239
|
+
}]
|
240
|
+
}, {
|
241
|
+
'type' => 'list_item',
|
242
|
+
'content' => [{
|
243
|
+
'type' => 'paragraph',
|
244
|
+
'content' => [{
|
245
|
+
'type' => 'text',
|
246
|
+
'text' => 'Second item with '
|
247
|
+
}, {
|
248
|
+
'type' => 'text',
|
249
|
+
'text' => 'emphasis',
|
250
|
+
'marks' => [{ 'type' => 'italic' }]
|
251
|
+
}]
|
252
|
+
}]
|
253
|
+
}]
|
254
|
+
}]
|
255
|
+
}
|
256
|
+
|
257
|
+
expect(document.to_h).to eq(expected)
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'creates a document with code blocks and blockquotes' do
|
261
|
+
document = described_class.create
|
262
|
+
wrapper = document.add_code_block_wrapper
|
263
|
+
wrapper.line_numbers = true
|
264
|
+
|
265
|
+
code_block = wrapper.add_code_block
|
266
|
+
code_block.language = 'ruby'
|
267
|
+
code_block.content = "def example\n puts 'Hello'\nend"
|
268
|
+
|
269
|
+
quote = document.add_blockquote
|
270
|
+
quote.citation = 'https://example.com'
|
271
|
+
quote.add_paragraph('A test quote')
|
272
|
+
|
273
|
+
expected = {
|
274
|
+
'type' => 'doc',
|
275
|
+
'content' => [{
|
276
|
+
'type' => 'code_block_wrapper',
|
277
|
+
'attrs' => {
|
278
|
+
'line_numbers' => true
|
279
|
+
},
|
280
|
+
'content' => [{
|
281
|
+
'type' => 'code_block',
|
282
|
+
'attrs' => {
|
283
|
+
'content' => "def example\n puts 'Hello'\nend",
|
284
|
+
'language' => 'ruby'
|
285
|
+
}
|
286
|
+
}]
|
287
|
+
}, {
|
288
|
+
'type' => 'blockquote',
|
289
|
+
'attrs' => {
|
290
|
+
'citation' => 'https://example.com'
|
291
|
+
},
|
292
|
+
'content' => [{
|
293
|
+
'type' => 'paragraph',
|
294
|
+
'content' => [{
|
295
|
+
'type' => 'text',
|
296
|
+
'text' => 'A test quote'
|
297
|
+
}]
|
298
|
+
}]
|
299
|
+
}]
|
300
|
+
}
|
301
|
+
|
302
|
+
expect(document.to_h).to eq(expected)
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'creates a document with mathematical content' do
|
306
|
+
document = described_class.new
|
307
|
+
|
308
|
+
# Add a heading
|
309
|
+
heading = document.add_heading(2)
|
310
|
+
heading.add_text('Mathematical Expressions')
|
311
|
+
|
312
|
+
# Add equations
|
313
|
+
para1 = document.add_paragraph
|
314
|
+
para1.add_text('The quadratic formula: x = -b ± ')
|
315
|
+
para1.add_text('√', [Prosereflect::Mark::Bold.new])
|
316
|
+
para1.add_text('(b')
|
317
|
+
para1.add_text('2', [Prosereflect::Mark::Superscript.new])
|
318
|
+
para1.add_text(' - 4ac) / 2a')
|
319
|
+
|
320
|
+
para2 = document.add_paragraph
|
321
|
+
para2.add_text('Water molecule: H')
|
322
|
+
para2.add_text('2', [Prosereflect::Mark::Subscript.new])
|
323
|
+
para2.add_text('O')
|
324
|
+
|
325
|
+
expected = {
|
326
|
+
'type' => 'doc',
|
327
|
+
'content' => [{
|
328
|
+
'type' => 'heading',
|
329
|
+
'attrs' => { 'level' => 2 },
|
330
|
+
'content' => [{
|
331
|
+
'type' => 'text',
|
332
|
+
'text' => 'Mathematical Expressions'
|
333
|
+
}]
|
334
|
+
}, {
|
335
|
+
'type' => 'paragraph',
|
336
|
+
'content' => [{
|
337
|
+
'type' => 'text',
|
338
|
+
'text' => 'The quadratic formula: x = -b ± '
|
339
|
+
}, {
|
340
|
+
'type' => 'text',
|
341
|
+
'text' => '√',
|
342
|
+
'marks' => [{ 'type' => 'bold' }]
|
343
|
+
}, {
|
344
|
+
'type' => 'text',
|
345
|
+
'text' => '(b'
|
346
|
+
}, {
|
347
|
+
'type' => 'text',
|
348
|
+
'text' => '2',
|
349
|
+
'marks' => [{ 'type' => 'superscript' }]
|
350
|
+
}, {
|
351
|
+
'type' => 'text',
|
352
|
+
'text' => ' - 4ac) / 2a'
|
353
|
+
}]
|
354
|
+
}, {
|
355
|
+
'type' => 'paragraph',
|
356
|
+
'content' => [{
|
357
|
+
'type' => 'text',
|
358
|
+
'text' => 'Water molecule: H'
|
359
|
+
}, {
|
360
|
+
'type' => 'text',
|
361
|
+
'text' => '2',
|
362
|
+
'marks' => [{ 'type' => 'subscript' }]
|
363
|
+
}, {
|
364
|
+
'type' => 'text',
|
365
|
+
'text' => 'O'
|
366
|
+
}]
|
367
|
+
}]
|
368
|
+
}
|
369
|
+
|
370
|
+
expect(document.to_h).to eq(expected)
|
27
371
|
end
|
28
372
|
|
29
|
-
it 'creates a document with
|
30
|
-
|
31
|
-
|
32
|
-
|
373
|
+
it 'creates a document with images and links' do
|
374
|
+
document = described_class.new
|
375
|
+
|
376
|
+
# Add an image
|
377
|
+
image = document.add_image('example.jpg', 'Example image')
|
378
|
+
image.title = 'A beautiful landscape'
|
379
|
+
image.width = 800
|
380
|
+
image.height = 600
|
381
|
+
|
382
|
+
# Add a paragraph with links
|
383
|
+
para = document.add_paragraph
|
384
|
+
para.add_text('Visit our ')
|
385
|
+
link_text = Prosereflect::Text.new(text: 'website')
|
386
|
+
link_mark = Prosereflect::Mark::Link.new
|
387
|
+
link_mark.attrs = { 'href' => 'https://example.com' }
|
388
|
+
link_text.marks = [link_mark]
|
389
|
+
para.add_child(link_text)
|
390
|
+
para.add_text(' for more information.')
|
391
|
+
|
392
|
+
expected = {
|
393
|
+
'type' => 'doc',
|
394
|
+
'content' => [{
|
395
|
+
'type' => 'image',
|
396
|
+
'attrs' => {
|
397
|
+
'src' => 'example.jpg',
|
398
|
+
'alt' => 'Example image',
|
399
|
+
'title' => 'A beautiful landscape',
|
400
|
+
'width' => 800,
|
401
|
+
'height' => 600
|
402
|
+
}
|
403
|
+
}, {
|
404
|
+
'type' => 'paragraph',
|
405
|
+
'content' => [{
|
406
|
+
'type' => 'text',
|
407
|
+
'text' => 'Visit our '
|
408
|
+
}, {
|
409
|
+
'type' => 'text',
|
410
|
+
'text' => 'website',
|
411
|
+
'marks' => [{
|
412
|
+
'type' => 'link',
|
413
|
+
'attrs' => {
|
414
|
+
'href' => 'https://example.com'
|
415
|
+
}
|
416
|
+
}]
|
417
|
+
}, {
|
418
|
+
'type' => 'text',
|
419
|
+
'text' => ' for more information.'
|
420
|
+
}]
|
421
|
+
}]
|
422
|
+
}
|
423
|
+
|
424
|
+
expect(document.to_h).to eq(expected)
|
33
425
|
end
|
34
426
|
end
|
35
427
|
|
36
428
|
describe '#paragraphs' do
|
37
429
|
it 'returns all paragraphs in the document' do
|
38
|
-
doc = described_class.
|
430
|
+
doc = described_class.new
|
39
431
|
doc.add_paragraph('First paragraph')
|
40
432
|
doc.add_paragraph('Second paragraph')
|
41
433
|
|
@@ -46,7 +438,7 @@ RSpec.describe Prosereflect::Document do
|
|
46
438
|
|
47
439
|
describe '#tables' do
|
48
440
|
it 'returns all tables in the document' do
|
49
|
-
doc = described_class.
|
441
|
+
doc = described_class.new
|
50
442
|
doc.add_table
|
51
443
|
doc.add_table
|
52
444
|
|
@@ -55,27 +447,9 @@ RSpec.describe Prosereflect::Document do
|
|
55
447
|
end
|
56
448
|
end
|
57
449
|
|
58
|
-
describe '#first_table' do
|
59
|
-
it 'returns the first table in the document' do
|
60
|
-
doc = described_class.create
|
61
|
-
table1 = doc.add_table
|
62
|
-
table1.add_header(['T1 Header'])
|
63
|
-
|
64
|
-
table2 = doc.add_table
|
65
|
-
table2.add_header(['T2 Header'])
|
66
|
-
|
67
|
-
expect(doc.first_table).to eq(table1)
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'returns nil if no tables exist' do
|
71
|
-
doc = described_class.create
|
72
|
-
expect(doc.first_table).to be_nil
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
450
|
describe '#add_paragraph' do
|
77
451
|
it 'adds a paragraph with text' do
|
78
|
-
doc = described_class.
|
452
|
+
doc = described_class.new
|
79
453
|
para = doc.add_paragraph('Test paragraph')
|
80
454
|
|
81
455
|
expect(doc.paragraphs.size).to eq(1)
|
@@ -84,7 +458,7 @@ RSpec.describe Prosereflect::Document do
|
|
84
458
|
end
|
85
459
|
|
86
460
|
it 'adds an empty paragraph' do
|
87
|
-
doc = described_class.
|
461
|
+
doc = described_class.new
|
88
462
|
para = doc.add_paragraph
|
89
463
|
|
90
464
|
expect(doc.paragraphs.size).to eq(1)
|
@@ -95,7 +469,7 @@ RSpec.describe Prosereflect::Document do
|
|
95
469
|
|
96
470
|
describe '#add_table' do
|
97
471
|
it 'adds a table to the document' do
|
98
|
-
doc = described_class.
|
472
|
+
doc = described_class.new
|
99
473
|
table = doc.add_table
|
100
474
|
|
101
475
|
expect(doc.tables.size).to eq(1)
|
@@ -103,7 +477,7 @@ RSpec.describe Prosereflect::Document do
|
|
103
477
|
end
|
104
478
|
|
105
479
|
it 'adds a table with attributes' do
|
106
|
-
doc = described_class.
|
480
|
+
doc = described_class.new
|
107
481
|
attrs = { 'width' => '100%' }
|
108
482
|
table = doc.add_table(attrs)
|
109
483
|
|
@@ -113,7 +487,7 @@ RSpec.describe Prosereflect::Document do
|
|
113
487
|
|
114
488
|
describe 'serialization' do
|
115
489
|
it 'converts to hash representation' do
|
116
|
-
doc = described_class.
|
490
|
+
doc = described_class.new
|
117
491
|
doc.add_paragraph('Test paragraph')
|
118
492
|
table = doc.add_table
|
119
493
|
table.add_header(['Header'])
|
@@ -127,7 +501,7 @@ RSpec.describe Prosereflect::Document do
|
|
127
501
|
end
|
128
502
|
|
129
503
|
it 'converts to YAML' do
|
130
|
-
doc = described_class.
|
504
|
+
doc = described_class.new
|
131
505
|
doc.add_paragraph('Test paragraph')
|
132
506
|
|
133
507
|
yaml = doc.to_yaml
|
@@ -137,7 +511,7 @@ RSpec.describe Prosereflect::Document do
|
|
137
511
|
end
|
138
512
|
|
139
513
|
it 'converts to JSON' do
|
140
|
-
doc = described_class.
|
514
|
+
doc = described_class.new
|
141
515
|
doc.add_paragraph('Test paragraph')
|
142
516
|
|
143
517
|
json = doc.to_json
|
@@ -146,4 +520,30 @@ RSpec.describe Prosereflect::Document do
|
|
146
520
|
expect(json).to include('"type":"paragraph"')
|
147
521
|
end
|
148
522
|
end
|
523
|
+
|
524
|
+
describe '#text_content' do
|
525
|
+
it 'returns plain text content from all nodes' do
|
526
|
+
document = described_class.new
|
527
|
+
|
528
|
+
# Add various types of content
|
529
|
+
document.add_heading(1).add_text('Title')
|
530
|
+
|
531
|
+
para = document.add_paragraph
|
532
|
+
para.add_text('This is ')
|
533
|
+
para.add_text('bold', [Prosereflect::Mark::Bold.new])
|
534
|
+
para.add_text(' text.')
|
535
|
+
|
536
|
+
list = document.add_bullet_list
|
537
|
+
list.add_item('First item')
|
538
|
+
list.add_item('Second item')
|
539
|
+
|
540
|
+
expected_text = "Title\nThis is bold text.\nFirst item\nSecond item"
|
541
|
+
expect(document.text_content).to eq(expected_text)
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'handles empty documents' do
|
545
|
+
document = described_class.new
|
546
|
+
expect(document.text_content).to eq('')
|
547
|
+
end
|
548
|
+
end
|
149
549
|
end
|