prosereflect 0.1.1 → 0.3.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 +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +97 -0
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +19 -1
- data/.rubocop_todo.yml +119 -183
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -4
- data/README.adoc +2 -0
- data/Rakefile +3 -3
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +45 -0
- data/docs/_advanced/index.adoc +15 -0
- data/docs/_advanced/schema.adoc +112 -0
- data/docs/_advanced/step-map.adoc +66 -0
- data/docs/_advanced/steps.adoc +88 -0
- data/docs/_advanced/test-builder.adoc +61 -0
- data/docs/_advanced/transform.adoc +92 -0
- data/docs/_config.yml +174 -0
- data/docs/_features/html-input.adoc +69 -0
- data/docs/_features/html-output.adoc +45 -0
- data/docs/_features/index.adoc +15 -0
- data/docs/_features/marks.adoc +86 -0
- data/docs/_features/node-types.adoc +124 -0
- data/docs/_features/user-mentions.adoc +47 -0
- data/docs/_guides/custom-nodes.adoc +107 -0
- data/docs/_guides/index.adoc +13 -0
- data/docs/_guides/round-trip-html.adoc +91 -0
- data/docs/_guides/serialization.adoc +109 -0
- data/docs/_pages/index.adoc +67 -0
- data/docs/_reference/document-api.adoc +49 -0
- data/docs/_reference/index.adoc +14 -0
- data/docs/_reference/node-api.adoc +79 -0
- data/docs/_reference/schema-api.adoc +95 -0
- data/docs/_reference/transform-api.adoc +77 -0
- data/docs/_understanding/document-model.adoc +65 -0
- data/docs/_understanding/fragment.adoc +52 -0
- data/docs/_understanding/index.adoc +14 -0
- data/docs/_understanding/resolved-position.adoc +53 -0
- data/docs/_understanding/slice.adoc +54 -0
- data/docs/lychee.toml +63 -0
- data/lib/prosereflect/attribute/base.rb +4 -6
- data/lib/prosereflect/attribute/bold.rb +2 -4
- data/lib/prosereflect/attribute/href.rb +1 -3
- data/lib/prosereflect/attribute/id.rb +7 -7
- data/lib/prosereflect/attribute.rb +4 -7
- data/lib/prosereflect/blockquote.rb +19 -11
- data/lib/prosereflect/bullet_list.rb +36 -29
- data/lib/prosereflect/code_block.rb +23 -27
- data/lib/prosereflect/code_block_wrapper.rb +12 -13
- data/lib/prosereflect/document.rb +14 -22
- data/lib/prosereflect/fragment.rb +249 -0
- data/lib/prosereflect/hard_break.rb +6 -6
- data/lib/prosereflect/heading.rb +14 -15
- data/lib/prosereflect/horizontal_rule.rb +23 -14
- data/lib/prosereflect/image.rb +32 -23
- data/lib/prosereflect/input/html.rb +179 -104
- data/lib/prosereflect/input.rb +7 -0
- data/lib/prosereflect/list_item.rb +11 -12
- data/lib/prosereflect/mark/base.rb +9 -11
- data/lib/prosereflect/mark/bold.rb +1 -3
- data/lib/prosereflect/mark/code.rb +1 -3
- data/lib/prosereflect/mark/italic.rb +1 -3
- data/lib/prosereflect/mark/link.rb +1 -3
- data/lib/prosereflect/mark/strike.rb +1 -3
- data/lib/prosereflect/mark/subscript.rb +1 -3
- data/lib/prosereflect/mark/superscript.rb +1 -3
- data/lib/prosereflect/mark/underline.rb +1 -3
- data/lib/prosereflect/mark.rb +9 -5
- data/lib/prosereflect/node.rb +171 -33
- data/lib/prosereflect/ordered_list.rb +17 -14
- data/lib/prosereflect/output/html.rb +279 -50
- data/lib/prosereflect/output.rb +7 -0
- data/lib/prosereflect/paragraph.rb +11 -13
- data/lib/prosereflect/parser.rb +56 -66
- data/lib/prosereflect/resolved_pos.rb +256 -0
- data/lib/prosereflect/schema/attribute.rb +57 -0
- data/lib/prosereflect/schema/content_match.rb +656 -0
- data/lib/prosereflect/schema/fragment.rb +166 -0
- data/lib/prosereflect/schema/mark.rb +121 -0
- data/lib/prosereflect/schema/mark_type.rb +130 -0
- data/lib/prosereflect/schema/node.rb +236 -0
- data/lib/prosereflect/schema/node_type.rb +274 -0
- data/lib/prosereflect/schema/schema_main.rb +190 -0
- data/lib/prosereflect/schema/spec.rb +92 -0
- data/lib/prosereflect/schema.rb +39 -0
- data/lib/prosereflect/table.rb +12 -13
- data/lib/prosereflect/table_cell.rb +13 -13
- data/lib/prosereflect/table_header.rb +17 -17
- data/lib/prosereflect/table_row.rb +12 -12
- data/lib/prosereflect/text.rb +35 -11
- data/lib/prosereflect/transform/attr_step.rb +157 -0
- data/lib/prosereflect/transform/insert_step.rb +115 -0
- data/lib/prosereflect/transform/mapping.rb +82 -0
- data/lib/prosereflect/transform/mark_step.rb +269 -0
- data/lib/prosereflect/transform/replace_around_step.rb +181 -0
- data/lib/prosereflect/transform/replace_step.rb +157 -0
- data/lib/prosereflect/transform/slice.rb +91 -0
- data/lib/prosereflect/transform/step.rb +89 -0
- data/lib/prosereflect/transform/step_map.rb +126 -0
- data/lib/prosereflect/transform/structure.rb +120 -0
- data/lib/prosereflect/transform/transform.rb +341 -0
- data/lib/prosereflect/transform.rb +26 -0
- data/lib/prosereflect/user.rb +15 -15
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +30 -17
- data/prosereflect.gemspec +17 -16
- data/spec/fixtures/documents/formatted_text.yaml +14 -0
- data/spec/fixtures/documents/heading_paragraph.yaml +16 -0
- data/spec/fixtures/documents/lists_doc.yaml +32 -0
- data/spec/fixtures/documents/mixed_content.yaml +40 -0
- data/spec/fixtures/documents/nested_doc.yaml +20 -0
- data/spec/fixtures/documents/simple_doc.yaml +6 -0
- data/spec/fixtures/documents/table_doc.yaml +32 -0
- data/spec/fixtures/documents/transform_test.yaml +14 -0
- data/spec/fixtures/schema/custom_schema.rb +37 -0
- data/spec/fixtures/schema/test_schema.rb +46 -0
- data/spec/fixtures/test_builder/helpers.rb +212 -0
- data/spec/prosereflect/document_spec.rb +332 -330
- data/spec/prosereflect/fragment_spec.rb +273 -0
- data/spec/prosereflect/hard_break_spec.rb +125 -125
- data/spec/prosereflect/input/html_spec.rb +718 -522
- data/spec/prosereflect/node_spec.rb +311 -182
- data/spec/prosereflect/output/html_spec.rb +105 -105
- data/spec/prosereflect/output/whitespace_spec.rb +248 -0
- data/spec/prosereflect/paragraph_spec.rb +275 -274
- data/spec/prosereflect/parser/round_trip_spec.rb +472 -0
- data/spec/prosereflect/parser_spec.rb +185 -180
- data/spec/prosereflect/resolved_pos_spec.rb +74 -0
- data/spec/prosereflect/schema/conftest.rb +68 -0
- data/spec/prosereflect/schema/content_match_spec.rb +237 -0
- data/spec/prosereflect/schema/mark_spec.rb +274 -0
- data/spec/prosereflect/schema/mark_type_spec.rb +86 -0
- data/spec/prosereflect/schema/node_type_spec.rb +142 -0
- data/spec/prosereflect/schema/schema_spec.rb +194 -0
- data/spec/prosereflect/table_cell_spec.rb +183 -183
- data/spec/prosereflect/table_row_spec.rb +149 -149
- data/spec/prosereflect/table_spec.rb +320 -318
- data/spec/prosereflect/test_builder/marks_spec.rb +127 -0
- data/spec/prosereflect/text_spec.rb +133 -132
- data/spec/prosereflect/transform/equivalence_spec.rb +487 -0
- data/spec/prosereflect/transform/mapping_spec.rb +226 -0
- data/spec/prosereflect/transform/replace_spec.rb +832 -0
- data/spec/prosereflect/transform/replace_step_spec.rb +157 -0
- data/spec/prosereflect/transform/slice_spec.rb +48 -0
- data/spec/prosereflect/transform/step_map_spec.rb +70 -0
- data/spec/prosereflect/transform/step_spec.rb +211 -0
- data/spec/prosereflect/transform/structure_spec.rb +98 -0
- data/spec/prosereflect/transform/transform_spec.rb +238 -0
- data/spec/prosereflect/user_spec.rb +31 -28
- data/spec/prosereflect_spec.rb +28 -26
- data/spec/spec_helper.rb +7 -6
- data/spec/support/matchers.rb +6 -6
- data/spec/support/shared_examples.rb +49 -49
- metadata +96 -5
- data/spec/prosereflect/version_spec.rb +0 -11
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "spec_helper"
|
|
4
4
|
|
|
5
5
|
RSpec.describe Prosereflect::Parser do
|
|
6
|
-
let(:fixtures_path) { File.join(__dir__,
|
|
6
|
+
let(:fixtures_path) { File.join(__dir__, "..", "fixtures") }
|
|
7
7
|
|
|
8
|
-
describe
|
|
9
|
-
describe
|
|
10
|
-
it
|
|
8
|
+
describe "document parsing" do
|
|
9
|
+
describe ".parse_document" do
|
|
10
|
+
it "parses a simple document" do
|
|
11
11
|
data = {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
"type" => "doc",
|
|
13
|
+
"content" => [
|
|
14
14
|
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
]
|
|
15
|
+
"type" => "paragraph",
|
|
16
|
+
"content" => [
|
|
17
|
+
{ "type" => "text", "text" => "Hello world" },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
document = described_class.parse_document(data)
|
|
24
24
|
expect(document).to be_a(Prosereflect::Document)
|
|
25
|
-
expect(document.text_content).to eq(
|
|
25
|
+
expect(document.text_content).to eq("Hello world")
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
it
|
|
28
|
+
it "parses a complex document with multiple blocks" do
|
|
29
29
|
data = {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
"type" => "doc",
|
|
31
|
+
"content" => [
|
|
32
32
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
]
|
|
33
|
+
"type" => "paragraph",
|
|
34
|
+
"content" => [
|
|
35
|
+
{ "type" => "text", "text" => "First paragraph" },
|
|
36
|
+
],
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
"type" => "bullet_list",
|
|
40
|
+
"content" => [
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
"type" => "list_item",
|
|
43
|
+
"content" => [
|
|
44
44
|
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
]
|
|
45
|
+
"type" => "paragraph",
|
|
46
|
+
"content" => [
|
|
47
|
+
{ "type" => "text", "text" => "List item" },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
"type" => "blockquote",
|
|
56
|
+
"content" => [
|
|
57
57
|
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
}
|
|
65
|
-
]
|
|
58
|
+
"type" => "paragraph",
|
|
59
|
+
"content" => [
|
|
60
|
+
{ "type" => "text", "text" => "Quote text" },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
document = described_class.parse_document(data)
|
|
@@ -73,17 +73,17 @@ RSpec.describe Prosereflect::Parser do
|
|
|
73
73
|
expect(document.content[2]).to be_a(Prosereflect::Blockquote)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
context
|
|
77
|
-
it
|
|
78
|
-
Dir.glob(File.join(fixtures_path,
|
|
79
|
-
data = YAML.
|
|
76
|
+
context "with fixtures" do
|
|
77
|
+
it "parses all YAML fixtures successfully" do
|
|
78
|
+
Dir.glob(File.join(fixtures_path, "*/*.yaml")).each do |yaml_file|
|
|
79
|
+
data = YAML.safe_load_file(yaml_file)
|
|
80
80
|
document = described_class.parse_document(data)
|
|
81
81
|
expect(document).to be_a(Prosereflect::Document)
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
it
|
|
86
|
-
Dir.glob(File.join(fixtures_path,
|
|
85
|
+
it "parses all JSON fixtures successfully" do
|
|
86
|
+
Dir.glob(File.join(fixtures_path, "*/*.json")).each do |json_file|
|
|
87
87
|
data = JSON.parse(File.read(json_file))
|
|
88
88
|
document = described_class.parse_document(data)
|
|
89
89
|
expect(document).to be_a(Prosereflect::Document)
|
|
@@ -91,60 +91,64 @@ RSpec.describe Prosereflect::Parser do
|
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
context
|
|
95
|
-
it
|
|
96
|
-
expect
|
|
94
|
+
context "with invalid input" do
|
|
95
|
+
it "raises an error for nil input" do
|
|
96
|
+
expect do
|
|
97
|
+
described_class.parse_document(nil)
|
|
98
|
+
end.to raise_error(ArgumentError)
|
|
97
99
|
end
|
|
98
100
|
|
|
99
|
-
it
|
|
100
|
-
expect
|
|
101
|
+
it "raises an error for non-hash input" do
|
|
102
|
+
expect do
|
|
103
|
+
described_class.parse_document("not a hash")
|
|
104
|
+
end.to raise_error(ArgumentError)
|
|
101
105
|
end
|
|
102
106
|
|
|
103
|
-
it
|
|
104
|
-
data = {
|
|
107
|
+
it "wraps non-document nodes in a document" do
|
|
108
|
+
data = { "type" => "paragraph", "content" => [] }
|
|
105
109
|
document = described_class.parse_document(data)
|
|
106
110
|
expect(document).to be_a(Prosereflect::Document)
|
|
107
111
|
expect(document.content.first).to be_a(Prosereflect::Paragraph)
|
|
108
112
|
end
|
|
109
113
|
|
|
110
|
-
it
|
|
111
|
-
document = described_class.parse_document({
|
|
114
|
+
it "wraps generic nodes in a document" do
|
|
115
|
+
document = described_class.parse_document({ "content" => [] })
|
|
112
116
|
expect(document).to be_a(Prosereflect::Document)
|
|
113
117
|
expect(document.content.size).to eq(1)
|
|
114
118
|
expect(document.content.first).to be_a(Prosereflect::Node)
|
|
115
|
-
expect(document.content.first.type).to eq(
|
|
119
|
+
expect(document.content.first.type).to eq("node")
|
|
116
120
|
end
|
|
117
121
|
end
|
|
118
122
|
end
|
|
119
123
|
end
|
|
120
124
|
|
|
121
|
-
describe
|
|
122
|
-
describe
|
|
123
|
-
context
|
|
124
|
-
it
|
|
125
|
+
describe "node parsing" do
|
|
126
|
+
describe ".parse_node" do
|
|
127
|
+
context "basic nodes" do
|
|
128
|
+
it "parses text nodes" do
|
|
125
129
|
data = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
{
|
|
131
|
-
]
|
|
130
|
+
"type" => "text",
|
|
131
|
+
"text" => "Hello world",
|
|
132
|
+
"marks" => [
|
|
133
|
+
{ "type" => "bold" },
|
|
134
|
+
{ "type" => "italic" },
|
|
135
|
+
],
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
node = described_class.parse_node(data)
|
|
135
139
|
expect(node).to be_a(Prosereflect::Text)
|
|
136
|
-
expect(node.text).to eq(
|
|
137
|
-
expect(node.marks.map { |m| m[
|
|
140
|
+
expect(node.text).to eq("Hello world")
|
|
141
|
+
expect(node.marks.map { |m| m["type"] }).to eq(%w[bold italic])
|
|
138
142
|
end
|
|
139
143
|
|
|
140
|
-
it
|
|
144
|
+
it "parses paragraph nodes" do
|
|
141
145
|
data = {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
{
|
|
146
|
-
{
|
|
147
|
-
]
|
|
146
|
+
"type" => "paragraph",
|
|
147
|
+
"content" => [
|
|
148
|
+
{ "type" => "text", "text" => "First" },
|
|
149
|
+
{ "type" => "hard_break" },
|
|
150
|
+
{ "type" => "text", "text" => "Second" },
|
|
151
|
+
],
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
node = described_class.parse_node(data)
|
|
@@ -153,103 +157,104 @@ RSpec.describe Prosereflect::Parser do
|
|
|
153
157
|
expect(node.text_content).to eq("First\nSecond")
|
|
154
158
|
end
|
|
155
159
|
|
|
156
|
-
it
|
|
157
|
-
data = {
|
|
160
|
+
it "parses hard break nodes" do
|
|
161
|
+
data = { "type" => "hard_break" }
|
|
158
162
|
node = described_class.parse_node(data)
|
|
159
163
|
expect(node).to be_a(Prosereflect::HardBreak)
|
|
160
164
|
end
|
|
161
165
|
|
|
162
|
-
it
|
|
166
|
+
it "parses horizontal rule nodes" do
|
|
163
167
|
data = {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
168
|
+
"type" => "horizontal_rule",
|
|
169
|
+
"attrs" => {
|
|
170
|
+
"border_style" => "dashed",
|
|
171
|
+
"width" => "80%",
|
|
172
|
+
},
|
|
169
173
|
}
|
|
170
174
|
|
|
171
175
|
node = described_class.parse_node(data)
|
|
172
176
|
expect(node).to be_a(Prosereflect::HorizontalRule)
|
|
173
|
-
expect(node.style).to eq(
|
|
174
|
-
expect(node.width).to eq(
|
|
177
|
+
expect(node.style).to eq("dashed")
|
|
178
|
+
expect(node.width).to eq("80%")
|
|
175
179
|
end
|
|
176
180
|
end
|
|
177
181
|
|
|
178
|
-
context
|
|
179
|
-
it
|
|
182
|
+
context "list nodes" do
|
|
183
|
+
it "parses bullet lists" do
|
|
180
184
|
data = {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
"type" => "bullet_list",
|
|
186
|
+
"attrs" => { "bullet_style" => "square" },
|
|
187
|
+
"content" => [
|
|
184
188
|
{
|
|
185
|
-
|
|
186
|
-
|
|
189
|
+
"type" => "list_item",
|
|
190
|
+
"content" => [
|
|
187
191
|
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
}
|
|
193
|
-
]
|
|
192
|
+
"type" => "paragraph",
|
|
193
|
+
"content" => [{ "type" => "text", "text" => "Item 1" }],
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
node = described_class.parse_node(data)
|
|
197
201
|
expect(node).to be_a(Prosereflect::BulletList)
|
|
198
|
-
expect(node.bullet_style).to eq(
|
|
199
|
-
expect(node.items.first.text_content).to eq(
|
|
202
|
+
expect(node.bullet_style).to eq("square")
|
|
203
|
+
expect(node.items.first.text_content).to eq("Item 1")
|
|
200
204
|
end
|
|
201
205
|
|
|
202
|
-
it
|
|
206
|
+
it "parses ordered lists" do
|
|
203
207
|
data = {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
"type" => "ordered_list",
|
|
209
|
+
"attrs" => { "start" => 3 },
|
|
210
|
+
"content" => [
|
|
207
211
|
{
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
"type" => "list_item",
|
|
213
|
+
"content" => [
|
|
210
214
|
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
]
|
|
215
|
+
"type" => "paragraph",
|
|
216
|
+
"content" => [{ "type" => "text", "text" => "Item 3" }],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
217
221
|
}
|
|
218
222
|
|
|
219
223
|
node = described_class.parse_node(data)
|
|
220
224
|
expect(node).to be_a(Prosereflect::OrderedList)
|
|
221
225
|
expect(node.start).to eq(3)
|
|
222
|
-
expect(node.items.first.text_content).to eq(
|
|
226
|
+
expect(node.items.first.text_content).to eq("Item 3")
|
|
223
227
|
end
|
|
224
228
|
|
|
225
|
-
it
|
|
229
|
+
it "parses nested lists" do
|
|
226
230
|
data = {
|
|
227
|
-
|
|
228
|
-
|
|
231
|
+
"type" => "bullet_list",
|
|
232
|
+
"content" => [
|
|
229
233
|
{
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
"type" => "list_item",
|
|
235
|
+
"content" => [
|
|
232
236
|
{
|
|
233
|
-
|
|
234
|
-
|
|
237
|
+
"type" => "paragraph",
|
|
238
|
+
"content" => [{ "type" => "text", "text" => "Level 1" }],
|
|
235
239
|
},
|
|
236
240
|
{
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
"type" => "ordered_list",
|
|
242
|
+
"content" => [
|
|
239
243
|
{
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
"type" => "list_item",
|
|
245
|
+
"content" => [
|
|
242
246
|
{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
"type" => "paragraph",
|
|
248
|
+
"content" => [{ "type" => "text",
|
|
249
|
+
"text" => "Level 2" }],
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
],
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
node = described_class.parse_node(data)
|
|
@@ -259,79 +264,79 @@ RSpec.describe Prosereflect::Parser do
|
|
|
259
264
|
end
|
|
260
265
|
end
|
|
261
266
|
|
|
262
|
-
context
|
|
263
|
-
it
|
|
267
|
+
context "table nodes" do
|
|
268
|
+
it "parses tables with complex content" do
|
|
264
269
|
data = {
|
|
265
|
-
|
|
266
|
-
|
|
270
|
+
"type" => "table",
|
|
271
|
+
"content" => [
|
|
267
272
|
{
|
|
268
|
-
|
|
269
|
-
|
|
273
|
+
"type" => "table_row",
|
|
274
|
+
"content" => [
|
|
270
275
|
{
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
276
|
+
"type" => "table_cell",
|
|
277
|
+
"attrs" => { "colspan" => 2 },
|
|
278
|
+
"content" => [
|
|
274
279
|
{
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
"type" => "paragraph",
|
|
281
|
+
"content" => [
|
|
277
282
|
{
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
]
|
|
283
|
-
}
|
|
284
|
-
]
|
|
285
|
-
}
|
|
286
|
-
]
|
|
287
|
-
}
|
|
288
|
-
]
|
|
283
|
+
"type" => "text",
|
|
284
|
+
"text" => "Header",
|
|
285
|
+
"marks" => [{ "type" => "bold" }],
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
],
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
node = described_class.parse_node(data)
|
|
292
297
|
expect(node).to be_a(Prosereflect::Table)
|
|
293
|
-
expect(node.rows.first.cells.first.attrs[
|
|
294
|
-
expect(node.rows.first.cells.first.text_content).to eq(
|
|
298
|
+
expect(node.rows.first.cells.first.attrs["colspan"]).to eq(2)
|
|
299
|
+
expect(node.rows.first.cells.first.text_content).to eq("Header")
|
|
295
300
|
end
|
|
296
301
|
end
|
|
297
302
|
|
|
298
|
-
context
|
|
299
|
-
it
|
|
303
|
+
context "blockquote nodes" do
|
|
304
|
+
it "parses blockquotes with citation" do
|
|
300
305
|
data = {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
"type" => "blockquote",
|
|
307
|
+
"attrs" => { "cite" => "Author" },
|
|
308
|
+
"content" => [
|
|
304
309
|
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
]
|
|
310
|
+
"type" => "paragraph",
|
|
311
|
+
"content" => [{ "type" => "text", "text" => "Quote" }],
|
|
312
|
+
},
|
|
313
|
+
],
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
node = described_class.parse_node(data)
|
|
312
317
|
expect(node).to be_a(Prosereflect::Blockquote)
|
|
313
|
-
expect(node.citation).to eq(
|
|
314
|
-
expect(node.text_content).to eq(
|
|
318
|
+
expect(node.citation).to eq("Author")
|
|
319
|
+
expect(node.text_content).to eq("Quote")
|
|
315
320
|
end
|
|
316
321
|
end
|
|
317
322
|
|
|
318
|
-
context
|
|
319
|
-
it
|
|
320
|
-
data = {
|
|
323
|
+
context "with invalid input" do
|
|
324
|
+
it "returns generic node for unknown types" do
|
|
325
|
+
data = { "type" => "unknown_type" }
|
|
321
326
|
node = described_class.parse_node(data)
|
|
322
327
|
expect(node).to be_a(Prosereflect::Node)
|
|
323
|
-
expect(node.type).to eq(
|
|
328
|
+
expect(node.type).to eq("node")
|
|
324
329
|
end
|
|
325
330
|
|
|
326
|
-
it
|
|
327
|
-
data = {
|
|
331
|
+
it "handles missing content gracefully" do
|
|
332
|
+
data = { "type" => "paragraph" }
|
|
328
333
|
node = described_class.parse_node(data)
|
|
329
334
|
expect(node).to be_a(Prosereflect::Paragraph)
|
|
330
335
|
expect(node.content).to eq([])
|
|
331
336
|
end
|
|
332
337
|
|
|
333
|
-
it
|
|
334
|
-
data = {
|
|
338
|
+
it "handles missing attributes gracefully" do
|
|
339
|
+
data = { "type" => "table_cell" }
|
|
335
340
|
node = described_class.parse_node(data)
|
|
336
341
|
expect(node).to be_a(Prosereflect::TableCell)
|
|
337
342
|
expect(node.attrs).to be_nil
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Prosereflect::ResolvedPos do
|
|
6
|
+
let(:doc) { Prosereflect::Document.new }
|
|
7
|
+
let(:resolved) { doc.resolve(0) }
|
|
8
|
+
|
|
9
|
+
describe "creation" do
|
|
10
|
+
it "creates resolved position" do
|
|
11
|
+
expect(resolved).to be_a(described_class)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "has position" do
|
|
15
|
+
expect(resolved.pos).to eq(0)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "parent" do
|
|
20
|
+
it "returns parent node" do
|
|
21
|
+
expect(resolved.parent).to be_a(Prosereflect::Document)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "depth" do
|
|
26
|
+
it "returns depth" do
|
|
27
|
+
expect(resolved.depth).to be >= 0
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "index" do
|
|
32
|
+
it "returns index within parent" do
|
|
33
|
+
expect(resolved.index).to eq(0)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "start" do
|
|
38
|
+
it "returns start position" do
|
|
39
|
+
expect(resolved.start).to eq(0)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "parent_offset" do
|
|
44
|
+
it "calculates offset within parent" do
|
|
45
|
+
expect(resolved.parent_offset).to eq(0)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "block?" do
|
|
50
|
+
it "checks if at block level" do
|
|
51
|
+
expect(resolved.block?).to be(false)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "inline?" do
|
|
56
|
+
it "checks if at inline level" do
|
|
57
|
+
expect(resolved.inline?).to be(true)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "equality" do
|
|
62
|
+
it "compares equal positions" do
|
|
63
|
+
other = doc.resolve(0)
|
|
64
|
+
expect(resolved).to eq(other)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe "shared_depth" do
|
|
69
|
+
it "returns shared depth with another position" do
|
|
70
|
+
other = doc.resolve(0)
|
|
71
|
+
expect(resolved.shared_depth(other)).to be >= 0
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|