prosereflect 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.
- checksums.yaml +7 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +228 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +10 -0
- data/README.adoc +268 -0
- data/Rakefile +12 -0
- data/debug_loading.rb +34 -0
- data/lib/prosereflect/document.rb +63 -0
- data/lib/prosereflect/hard_break.rb +22 -0
- data/lib/prosereflect/node.rb +77 -0
- data/lib/prosereflect/paragraph.rb +50 -0
- data/lib/prosereflect/parser.rb +56 -0
- data/lib/prosereflect/table.rb +64 -0
- data/lib/prosereflect/table_cell.rb +37 -0
- data/lib/prosereflect/table_row.rb +32 -0
- data/lib/prosereflect/text.rb +37 -0
- data/lib/prosereflect/version.rb +5 -0
- data/lib/prosereflect.rb +17 -0
- data/prosereflect.gemspec +33 -0
- data/sig/prosemirror.rbs +4 -0
- data/spec/fixtures/ituob-1000/ituob-1000-DP.json +366 -0
- data/spec/fixtures/ituob-1000/ituob-1000-DP.yaml +182 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E118_IIN.json +381 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E118_IIN.yaml +182 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E164_ACN.json +203 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E164_ACN.yaml +98 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E212_MNC.json +202 -0
- data/spec/fixtures/ituob-1000/ituob-1000-E212_MNC.yaml +101 -0
- data/spec/fixtures/ituob-1000/ituob-1000-F32_TDI.json +6948 -0
- data/spec/fixtures/ituob-1000/ituob-1000-F32_TDI.yaml +3519 -0
- data/spec/fixtures/ituob-1000/ituob-1000-M1400_ICC.json +529 -0
- data/spec/fixtures/ituob-1000/ituob-1000-M1400_ICC.yaml +263 -0
- data/spec/fixtures/ituob-1000/ituob-1000-NNP.json +288 -0
- data/spec/fixtures/ituob-1000/ituob-1000-NNP.yaml +152 -0
- data/spec/fixtures/ituob-1000/ituob-1000-Q708_ISPC.json +1534 -0
- data/spec/fixtures/ituob-1000/ituob-1000-Q708_ISPC.yaml +789 -0
- data/spec/fixtures/ituob-1000/ituob-1000-Q708_SANC.json +252 -0
- data/spec/fixtures/ituob-1000/ituob-1000-Q708_SANC.yaml +123 -0
- data/spec/fixtures/ituob-1000/ituob-1000-R_SP_LM.V.json +428 -0
- data/spec/fixtures/ituob-1000/ituob-1000-R_SP_LM.V.yaml +208 -0
- data/spec/fixtures/ituob-1000/ituob-1000-T35_NA.json +621 -0
- data/spec/fixtures/ituob-1000/ituob-1000-T35_NA.yaml +317 -0
- data/spec/fixtures/ituob-1001/ituob-1001-DP.json +532 -0
- data/spec/fixtures/ituob-1001/ituob-1001-DP.yaml +266 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E118_IIN.json +1093 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E118_IIN.yaml +519 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E164_ACN.json +449 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E164_ACN.yaml +214 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E164_CC.json +271 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E164_CC.yaml +136 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E212_MNC.json +199 -0
- data/spec/fixtures/ituob-1001/ituob-1001-E212_MNC.yaml +99 -0
- data/spec/fixtures/ituob-1001/ituob-1001-NNP.json +288 -0
- data/spec/fixtures/ituob-1001/ituob-1001-NNP.yaml +152 -0
- data/spec/fixtures/ituob-1001/ituob-1001-Q708_ISPC.json +960 -0
- data/spec/fixtures/ituob-1001/ituob-1001-Q708_ISPC.yaml +487 -0
- data/spec/prosereflect/document_spec.rb +149 -0
- data/spec/prosereflect/hard_break_spec.rb +51 -0
- data/spec/prosereflect/node_spec.rb +256 -0
- data/spec/prosereflect/paragraph_spec.rb +152 -0
- data/spec/prosereflect/parser_spec.rb +129 -0
- data/spec/prosereflect/table_cell_spec.rb +114 -0
- data/spec/prosereflect/table_row_spec.rb +77 -0
- data/spec/prosereflect/table_spec.rb +144 -0
- data/spec/prosereflect/text_spec.rb +116 -0
- data/spec/prosereflect/version_spec.rb +11 -0
- data/spec/prosereflect_spec.rb +57 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/matchers.rb +131 -0
- data/spec/support/shared_examples.rb +220 -0
- metadata +133 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Prosereflect::Node do
|
4
|
+
describe 'initialization' do
|
5
|
+
it 'initializes with empty data' do
|
6
|
+
node = described_class.new
|
7
|
+
expect(node.type).to be_nil
|
8
|
+
expect(node.attrs).to be_nil
|
9
|
+
expect(node.content).to eq([])
|
10
|
+
expect(node.marks).to be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'initializes with provided data' do
|
14
|
+
data = {
|
15
|
+
'type' => 'test_node',
|
16
|
+
'attrs' => { 'key' => 'value' },
|
17
|
+
'marks' => [{ 'type' => 'bold' }]
|
18
|
+
}
|
19
|
+
node = described_class.new(data)
|
20
|
+
expect(node.type).to eq('test_node')
|
21
|
+
expect(node.attrs).to eq({ 'key' => 'value' })
|
22
|
+
expect(node.marks).to eq([{ 'type' => 'bold' }])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#parse_content' do
|
27
|
+
it 'returns empty array for nil content' do
|
28
|
+
node = described_class.new
|
29
|
+
expect(node.parse_content(nil)).to eq([])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses content items using Parser' do
|
33
|
+
content_data = [
|
34
|
+
{ 'type' => 'text', 'text' => 'Hello' },
|
35
|
+
{ 'type' => 'hard_break' }
|
36
|
+
]
|
37
|
+
|
38
|
+
node = described_class.new
|
39
|
+
parsed_content = node.parse_content(content_data)
|
40
|
+
|
41
|
+
expect(parsed_content.size).to eq(2)
|
42
|
+
expect(parsed_content[0]).to be_a(Prosereflect::Text)
|
43
|
+
expect(parsed_content[1]).to be_a(Prosereflect::HardBreak)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#to_h' do
|
48
|
+
it 'creates a hash representation with basic properties' do
|
49
|
+
node = described_class.new({ 'type' => 'test_node' })
|
50
|
+
hash = node.to_h
|
51
|
+
|
52
|
+
expect(hash).to be_a(Hash)
|
53
|
+
expect(hash['type']).to eq('test_node')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'includes attrs when present' do
|
57
|
+
node = described_class.new({
|
58
|
+
'type' => 'test_node',
|
59
|
+
'attrs' => { 'key' => 'value' }
|
60
|
+
})
|
61
|
+
|
62
|
+
hash = node.to_h
|
63
|
+
expect(hash['attrs']).to eq({ 'key' => 'value' })
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'includes marks when present' do
|
67
|
+
node = described_class.new({
|
68
|
+
'type' => 'test_node',
|
69
|
+
'marks' => [{ 'type' => 'bold' }]
|
70
|
+
})
|
71
|
+
|
72
|
+
hash = node.to_h
|
73
|
+
expect(hash['marks']).to eq([{ 'type' => 'bold' }])
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'includes content when present' do
|
77
|
+
node = described_class.new({
|
78
|
+
'type' => 'test_node',
|
79
|
+
'content' => [{ 'type' => 'text', 'text' => 'Hello' }]
|
80
|
+
})
|
81
|
+
|
82
|
+
hash = node.to_h
|
83
|
+
expect(hash['content']).to be_an(Array)
|
84
|
+
expect(hash['content'][0]['type']).to eq('text')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#add_child' do
|
89
|
+
it 'adds a child node to content' do
|
90
|
+
parent = described_class.new({ 'type' => 'parent' })
|
91
|
+
child = described_class.new({ 'type' => 'child' })
|
92
|
+
|
93
|
+
parent.add_child(child)
|
94
|
+
|
95
|
+
expect(parent.content.size).to eq(1)
|
96
|
+
expect(parent.content[0]).to eq(child)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns the added child' do
|
100
|
+
parent = described_class.new({ 'type' => 'parent' })
|
101
|
+
child = described_class.new({ 'type' => 'child' })
|
102
|
+
|
103
|
+
result = parent.add_child(child)
|
104
|
+
|
105
|
+
expect(result).to eq(child)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#find_first' do
|
110
|
+
let(:node) do
|
111
|
+
root = described_class.new({ 'type' => 'root' })
|
112
|
+
para = Prosereflect::Paragraph.new({ 'type' => 'paragraph' })
|
113
|
+
text = Prosereflect::Text.new({ 'type' => 'text', 'text' => 'Hello' })
|
114
|
+
|
115
|
+
para.add_child(text)
|
116
|
+
root.add_child(para)
|
117
|
+
root
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns self if type matches' do
|
121
|
+
result = node.find_first('root')
|
122
|
+
expect(result).to eq(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'finds a child node by type' do
|
126
|
+
result = node.find_first('paragraph')
|
127
|
+
expect(result).to be_a(Prosereflect::Paragraph)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'finds a nested node by type' do
|
131
|
+
result = node.find_first('text')
|
132
|
+
expect(result).to be_a(Prosereflect::Text)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns nil if no matching node is found' do
|
136
|
+
result = node.find_first('nonexistent')
|
137
|
+
expect(result).to be_nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '.create' do
|
142
|
+
it 'creates a node with the specified type' do
|
143
|
+
node = described_class.create('test_node')
|
144
|
+
expect(node.type).to eq('test_node')
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'creates a node with attributes' do
|
148
|
+
attrs = { 'key' => 'value' }
|
149
|
+
node = described_class.create('test_node', attrs)
|
150
|
+
|
151
|
+
expect(node.type).to eq('test_node')
|
152
|
+
expect(node.attrs).to eq(attrs)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'initializes with empty content' do
|
156
|
+
node = described_class.create('test_node')
|
157
|
+
expect(node.content).to eq([])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#find_all' do
|
162
|
+
let(:node) do
|
163
|
+
root = described_class.new({ 'type' => 'root' })
|
164
|
+
|
165
|
+
para1 = Prosereflect::Paragraph.new({ 'type' => 'paragraph' })
|
166
|
+
para1.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'Text 1' }))
|
167
|
+
|
168
|
+
para2 = Prosereflect::Paragraph.new({ 'type' => 'paragraph' })
|
169
|
+
para2.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'Text 2' }))
|
170
|
+
|
171
|
+
root.add_child(para1)
|
172
|
+
root.add_child(para2)
|
173
|
+
root
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'finds all nodes of a specific type' do
|
177
|
+
paragraphs = node.find_all('paragraph')
|
178
|
+
expect(paragraphs.size).to eq(2)
|
179
|
+
expect(paragraphs).to all(be_a(Prosereflect::Paragraph))
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'finds all nested nodes of a specific type' do
|
183
|
+
texts = node.find_all('text')
|
184
|
+
expect(texts.size).to eq(2)
|
185
|
+
expect(texts).to all(be_a(Prosereflect::Text))
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'returns empty array if no matching nodes are found' do
|
189
|
+
result = node.find_all('nonexistent')
|
190
|
+
expect(result).to eq([])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#find_children' do
|
195
|
+
let(:node) do
|
196
|
+
root = described_class.new({ 'type' => 'root' })
|
197
|
+
|
198
|
+
root.add_child(Prosereflect::Paragraph.new({ 'type' => 'paragraph' }))
|
199
|
+
root.add_child(Prosereflect::Table.new({ 'type' => 'table' }))
|
200
|
+
root.add_child(Prosereflect::Paragraph.new({ 'type' => 'paragraph' }))
|
201
|
+
|
202
|
+
root
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'finds direct children of a specific type' do
|
206
|
+
paragraphs = node.find_children('paragraph')
|
207
|
+
expect(paragraphs.size).to eq(2)
|
208
|
+
expect(paragraphs).to all(be_a(Prosereflect::Paragraph))
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'returns empty array if no matching children are found' do
|
212
|
+
result = node.find_children('nonexistent')
|
213
|
+
expect(result).to eq([])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe '#text_content' do
|
218
|
+
it 'returns empty string for node without content' do
|
219
|
+
node = described_class.new({ 'type' => 'empty' })
|
220
|
+
expect(node.text_content).to eq('')
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'concatenates text content from all child nodes' do
|
224
|
+
node = described_class.new({ 'type' => 'parent' })
|
225
|
+
|
226
|
+
para = Prosereflect::Paragraph.new({ 'type' => 'paragraph' })
|
227
|
+
para.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'Hello' }))
|
228
|
+
para.add_child(Prosereflect::HardBreak.new({ 'type' => 'hard_break' }))
|
229
|
+
para.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'World' }))
|
230
|
+
|
231
|
+
node.add_child(para)
|
232
|
+
|
233
|
+
expect(node.text_content).to eq("Hello\nWorld")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe '#text_content_with_breaks' do
|
238
|
+
it 'returns empty string for node without content' do
|
239
|
+
node = described_class.new({ 'type' => 'empty' })
|
240
|
+
expect(node.text_content_with_breaks).to eq('')
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'includes newlines for hard breaks' do
|
244
|
+
node = described_class.new({ 'type' => 'parent' })
|
245
|
+
|
246
|
+
para = Prosereflect::Paragraph.new({ 'type' => 'paragraph' })
|
247
|
+
para.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'Hello' }))
|
248
|
+
para.add_child(Prosereflect::HardBreak.new({ 'type' => 'hard_break' }))
|
249
|
+
para.add_child(Prosereflect::Text.new({ 'type' => 'text', 'text' => 'World' }))
|
250
|
+
|
251
|
+
node.add_child(para)
|
252
|
+
|
253
|
+
expect(node.text_content_with_breaks).to eq("Hello\nWorld")
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Prosereflect::Paragraph do
|
6
|
+
describe 'initialization' do
|
7
|
+
it 'initializes as a paragraph node' do
|
8
|
+
paragraph = described_class.new({ 'type' => 'paragraph' })
|
9
|
+
expect(paragraph.type).to eq('paragraph')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.create' do
|
14
|
+
it 'creates an empty paragraph' do
|
15
|
+
paragraph = described_class.create
|
16
|
+
expect(paragraph).to be_a(described_class)
|
17
|
+
expect(paragraph.type).to eq('paragraph')
|
18
|
+
expect(paragraph.content).to be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'creates a paragraph with attributes' do
|
22
|
+
attrs = { 'align' => 'center' }
|
23
|
+
paragraph = described_class.create(attrs)
|
24
|
+
expect(paragraph.attrs).to eq(attrs)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#text_nodes' do
|
29
|
+
it 'returns all text nodes in the paragraph' do
|
30
|
+
paragraph = described_class.create
|
31
|
+
paragraph.add_text('First text')
|
32
|
+
paragraph.add_text('Second text')
|
33
|
+
|
34
|
+
expect(paragraph.text_nodes.size).to eq(2)
|
35
|
+
expect(paragraph.text_nodes).to all(be_a(Prosereflect::Text))
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns empty array for paragraph with no text nodes' do
|
39
|
+
paragraph = described_class.create
|
40
|
+
expect(paragraph.text_nodes).to eq([])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#text_content' do
|
45
|
+
it 'returns concatenated text from all text nodes' do
|
46
|
+
paragraph = described_class.create
|
47
|
+
paragraph.add_text('First text')
|
48
|
+
paragraph.add_text(' Second text')
|
49
|
+
|
50
|
+
expect(paragraph.text_content).to eq('First text Second text')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns empty string for empty paragraph' do
|
54
|
+
paragraph = described_class.create
|
55
|
+
expect(paragraph.text_content).to eq('')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'handles hard breaks' do
|
59
|
+
paragraph = described_class.create
|
60
|
+
paragraph.add_text('First line')
|
61
|
+
paragraph.add_hard_break
|
62
|
+
paragraph.add_text('Second line')
|
63
|
+
|
64
|
+
expect(paragraph.text_content).to eq("First line\nSecond line")
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'handles mixed content types' do
|
68
|
+
paragraph = described_class.create
|
69
|
+
paragraph.add_text('Text with ')
|
70
|
+
|
71
|
+
# Add a custom node that is neither text nor hard_break
|
72
|
+
custom_node = Prosereflect::Node.create('custom_node')
|
73
|
+
custom_node.instance_eval do
|
74
|
+
def text_content
|
75
|
+
'custom content'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
paragraph.add_child(custom_node)
|
79
|
+
|
80
|
+
expect(paragraph.text_content).to eq('Text with custom content')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#add_text' do
|
85
|
+
it 'adds text to the paragraph' do
|
86
|
+
paragraph = described_class.create
|
87
|
+
text_node = paragraph.add_text('Hello world')
|
88
|
+
|
89
|
+
expect(paragraph.content.size).to eq(1)
|
90
|
+
expect(text_node).to be_a(Prosereflect::Text)
|
91
|
+
expect(text_node.text).to eq('Hello world')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'adds text with marks' do
|
95
|
+
paragraph = described_class.create
|
96
|
+
marks = [{ 'type' => 'bold' }]
|
97
|
+
text_node = paragraph.add_text('Bold text', marks)
|
98
|
+
|
99
|
+
expect(text_node.marks).to eq(marks)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'does not add empty text' do
|
103
|
+
paragraph = described_class.create
|
104
|
+
result = paragraph.add_text('')
|
105
|
+
|
106
|
+
expect(paragraph.content).to be_empty
|
107
|
+
expect(result).to be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'does not add nil text' do
|
111
|
+
paragraph = described_class.create
|
112
|
+
result = paragraph.add_text(nil)
|
113
|
+
|
114
|
+
expect(paragraph.content).to be_empty
|
115
|
+
expect(result).to be_nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#add_hard_break' do
|
120
|
+
it 'adds a hard break to the paragraph' do
|
121
|
+
paragraph = described_class.create
|
122
|
+
hard_break = paragraph.add_hard_break
|
123
|
+
|
124
|
+
expect(paragraph.content.size).to eq(1)
|
125
|
+
expect(hard_break).to be_a(Prosereflect::HardBreak)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'adds a hard break with marks' do
|
129
|
+
paragraph = described_class.create
|
130
|
+
marks = [{ 'type' => 'italic' }]
|
131
|
+
hard_break = paragraph.add_hard_break(marks)
|
132
|
+
|
133
|
+
expect(hard_break.marks).to eq(marks)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'serialization' do
|
138
|
+
it 'converts to hash representation' do
|
139
|
+
paragraph = described_class.create
|
140
|
+
paragraph.add_text('Hello')
|
141
|
+
paragraph.add_hard_break
|
142
|
+
paragraph.add_text('World')
|
143
|
+
|
144
|
+
hash = paragraph.to_h
|
145
|
+
expect(hash['type']).to eq('paragraph')
|
146
|
+
expect(hash['content'].size).to eq(3)
|
147
|
+
expect(hash['content'][0]['type']).to eq('text')
|
148
|
+
expect(hash['content'][1]['type']).to eq('hard_break')
|
149
|
+
expect(hash['content'][2]['type']).to eq('text')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Prosereflect::Parser do
|
6
|
+
let(:fixtures_path) { File.join(__dir__, '..', 'fixtures') }
|
7
|
+
|
8
|
+
describe '.parse_document' do
|
9
|
+
context 'with YAML fixtures' do
|
10
|
+
let(:yaml_files) { Dir.glob(File.join(fixtures_path, '*/*.yaml')) }
|
11
|
+
|
12
|
+
it 'parses all YAML fixtures successfully' do
|
13
|
+
yaml_files.each do |yaml_file|
|
14
|
+
yaml_content = File.read(yaml_file)
|
15
|
+
data = YAML.safe_load(yaml_content)
|
16
|
+
|
17
|
+
expect do
|
18
|
+
document = described_class.parse_document(data)
|
19
|
+
expect(document).to be_a(Prosereflect::Document)
|
20
|
+
end.not_to raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with JSON fixtures' do
|
26
|
+
let(:json_files) { Dir.glob(File.join(fixtures_path, '*/*.json')) }
|
27
|
+
|
28
|
+
it 'parses all JSON fixtures successfully' do
|
29
|
+
json_files.each do |json_file|
|
30
|
+
json_content = File.read(json_file)
|
31
|
+
data = JSON.parse(json_content)
|
32
|
+
|
33
|
+
expect do
|
34
|
+
document = described_class.parse_document(data)
|
35
|
+
expect(document).to be_a(Prosereflect::Document)
|
36
|
+
end.not_to raise_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with invalid input' do
|
42
|
+
it 'raises an error for nil input' do
|
43
|
+
expect { described_class.parse_document(nil) }.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'raises an error for non-hash input' do
|
47
|
+
expect { described_class.parse_document('not a hash') }.to raise_error(ArgumentError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.parse_node' do
|
53
|
+
it 'creates the correct node type based on the input' do
|
54
|
+
# Test document node
|
55
|
+
doc_data = { 'type' => 'doc', 'content' => [] }
|
56
|
+
expect(described_class.parse_node(doc_data)).to be_a(Prosereflect::Document)
|
57
|
+
|
58
|
+
# Test paragraph node
|
59
|
+
para_data = { 'type' => 'paragraph', 'content' => [] }
|
60
|
+
expect(described_class.parse_node(para_data)).to be_a(Prosereflect::Paragraph)
|
61
|
+
|
62
|
+
# Test text node
|
63
|
+
text_data = { 'type' => 'text', 'text' => 'Hello' }
|
64
|
+
expect(described_class.parse_node(text_data)).to be_a(Prosereflect::Text)
|
65
|
+
|
66
|
+
# Test hard_break node
|
67
|
+
break_data = { 'type' => 'hard_break' }
|
68
|
+
expect(described_class.parse_node(break_data)).to be_a(Prosereflect::HardBreak)
|
69
|
+
|
70
|
+
# Test table node
|
71
|
+
table_data = { 'type' => 'table', 'content' => [] }
|
72
|
+
expect(described_class.parse_node(table_data)).to be_a(Prosereflect::Table)
|
73
|
+
|
74
|
+
# Test table_row node
|
75
|
+
row_data = { 'type' => 'table_row', 'content' => [] }
|
76
|
+
expect(described_class.parse_node(row_data)).to be_a(Prosereflect::TableRow)
|
77
|
+
|
78
|
+
# Test table_cell node
|
79
|
+
cell_data = { 'type' => 'table_cell', 'content' => [] }
|
80
|
+
expect(described_class.parse_node(cell_data)).to be_a(Prosereflect::TableCell)
|
81
|
+
|
82
|
+
# Test generic node
|
83
|
+
generic_data = { 'type' => 'unknown_type' }
|
84
|
+
expect(described_class.parse_node(generic_data)).to be_a(Prosereflect::Node)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'handles nodes with content' do
|
88
|
+
data = {
|
89
|
+
'type' => 'paragraph',
|
90
|
+
'content' => [
|
91
|
+
{ 'type' => 'text', 'text' => 'Hello' },
|
92
|
+
{ 'type' => 'hard_break' },
|
93
|
+
{ 'type' => 'text', 'text' => 'World' }
|
94
|
+
]
|
95
|
+
}
|
96
|
+
|
97
|
+
node = described_class.parse_node(data)
|
98
|
+
expect(node).to be_a(Prosereflect::Paragraph)
|
99
|
+
expect(node.content.size).to eq(3)
|
100
|
+
expect(node.content[0]).to be_a(Prosereflect::Text)
|
101
|
+
expect(node.content[1]).to be_a(Prosereflect::HardBreak)
|
102
|
+
expect(node.content[2]).to be_a(Prosereflect::Text)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'handles nodes with marks' do
|
106
|
+
data = {
|
107
|
+
'type' => 'text',
|
108
|
+
'text' => 'Bold text',
|
109
|
+
'marks' => [{ 'type' => 'bold' }]
|
110
|
+
}
|
111
|
+
|
112
|
+
node = described_class.parse_node(data)
|
113
|
+
expect(node).to be_a(Prosereflect::Text)
|
114
|
+
expect(node.marks).to eq([{ 'type' => 'bold' }])
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'handles nodes with attributes' do
|
118
|
+
data = {
|
119
|
+
'type' => 'table_cell',
|
120
|
+
'attrs' => { 'colspan' => 2, 'rowspan' => 1 },
|
121
|
+
'content' => []
|
122
|
+
}
|
123
|
+
|
124
|
+
node = described_class.parse_node(data)
|
125
|
+
expect(node).to be_a(Prosereflect::TableCell)
|
126
|
+
expect(node.attrs).to eq({ 'colspan' => 2, 'rowspan' => 1 })
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Prosereflect::TableCell do
|
6
|
+
describe 'initialization' do
|
7
|
+
it 'initializes as a table_cell node' do
|
8
|
+
cell = described_class.new({ 'type' => 'table_cell' })
|
9
|
+
expect(cell.type).to eq('table_cell')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.create' do
|
14
|
+
it 'creates an empty table cell' do
|
15
|
+
cell = described_class.create
|
16
|
+
expect(cell).to be_a(described_class)
|
17
|
+
expect(cell.type).to eq('table_cell')
|
18
|
+
expect(cell.content).to be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'creates a table cell with attributes' do
|
22
|
+
attrs = { 'colspan' => 2, 'rowspan' => 1 }
|
23
|
+
cell = described_class.create(attrs)
|
24
|
+
expect(cell.attrs).to eq(attrs)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#paragraphs' do
|
29
|
+
it 'returns all paragraphs in the cell' do
|
30
|
+
cell = described_class.create
|
31
|
+
cell.add_paragraph('Para 1')
|
32
|
+
cell.add_paragraph('Para 2')
|
33
|
+
|
34
|
+
expect(cell.paragraphs.size).to eq(2)
|
35
|
+
expect(cell.paragraphs).to all(be_a(Prosereflect::Paragraph))
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns empty array for cell with no paragraphs' do
|
39
|
+
cell = described_class.create
|
40
|
+
expect(cell.paragraphs).to eq([])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#text_content' do
|
45
|
+
it 'returns concatenated text from all paragraphs with newlines' do
|
46
|
+
cell = described_class.create
|
47
|
+
cell.add_paragraph('First paragraph')
|
48
|
+
cell.add_paragraph('Second paragraph')
|
49
|
+
|
50
|
+
expect(cell.text_content).to eq("First paragraph\nSecond paragraph")
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns empty string for empty cell' do
|
54
|
+
cell = described_class.create
|
55
|
+
expect(cell.text_content).to eq('')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#lines' do
|
60
|
+
it 'splits text content into lines' do
|
61
|
+
cell = described_class.create
|
62
|
+
cell.add_paragraph('Line 1')
|
63
|
+
cell.add_paragraph('Line 2')
|
64
|
+
|
65
|
+
expect(cell.lines).to eq(['Line 1', 'Line 2'])
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns empty array for empty cell' do
|
69
|
+
cell = described_class.create
|
70
|
+
expect(cell.lines).to eq([])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'handles multi-line paragraphs' do
|
74
|
+
cell = described_class.create
|
75
|
+
para = cell.add_paragraph('First line')
|
76
|
+
para.add_hard_break
|
77
|
+
para.add_text('Second line')
|
78
|
+
|
79
|
+
expect(cell.lines).to eq(['First line', 'Second line'])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#add_paragraph' do
|
84
|
+
it 'adds a paragraph with text' do
|
85
|
+
cell = described_class.create
|
86
|
+
paragraph = cell.add_paragraph('Test content')
|
87
|
+
|
88
|
+
expect(cell.paragraphs.size).to eq(1)
|
89
|
+
expect(paragraph).to be_a(Prosereflect::Paragraph)
|
90
|
+
expect(paragraph.text_content).to eq('Test content')
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'adds an empty paragraph' do
|
94
|
+
cell = described_class.create
|
95
|
+
paragraph = cell.add_paragraph
|
96
|
+
|
97
|
+
expect(cell.paragraphs.size).to eq(1)
|
98
|
+
expect(paragraph).to be_a(Prosereflect::Paragraph)
|
99
|
+
expect(paragraph.text_content).to eq('')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'serialization' do
|
104
|
+
it 'converts to hash representation' do
|
105
|
+
cell = described_class.create
|
106
|
+
cell.add_paragraph('Test content')
|
107
|
+
|
108
|
+
hash = cell.to_h
|
109
|
+
expect(hash['type']).to eq('table_cell')
|
110
|
+
expect(hash['content'].size).to eq(1)
|
111
|
+
expect(hash['content'][0]['type']).to eq('paragraph')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|