moxml 0.5.177 → 0.6.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/nightly-fuzz.yml +0 -14
- data/.github/workflows/performance-release.yml +0 -13
- data/.github/workflows/performance.yml +0 -17
- data/.github/workflows/rake.yml +0 -38
- data/.github/workflows/round-trip.yml +0 -45
- data/.github/workflows/windows.yml +0 -12
- data/Gemfile +4 -14
- data/README.adoc +5 -388
- data/Rakefile +2 -179
- data/docs/_references/processing-model.adoc +3 -2
- data/lib/moxml/adapter/customized_oga.rb +6 -4
- data/lib/moxml/adapter/customized_rexml.rb +5 -3
- data/lib/moxml/adapter/headed_ox.rb +0 -2
- data/lib/moxml/adapter/leptris.rb +43 -7
- data/lib/moxml/adapter/libxml.rb +0 -2
- data/lib/moxml/adapter/nokogiri.rb +0 -2
- data/lib/moxml/adapter/ox.rb +0 -2
- data/lib/moxml/adapter/rexml.rb +1 -3
- data/lib/moxml/adapter.rb +1 -6
- data/lib/moxml/config.rb +0 -17
- data/lib/moxml/entity_registry.rb +1 -5
- data/lib/moxml/native_attachment.rb +1 -3
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml.rb +1 -2
- data/spec/moxml/adapter/platform_spec.rb +2 -47
- data/spec/moxml/adapter_capabilities_spec.rb +25 -0
- data/spec/moxml/native_attachment_spec.rb +4 -50
- data/spec/moxml/signature/edge_cases_spec.rb +0 -1
- data/spec/spec_helper.rb +13 -31
- metadata +2 -22
- data/.github/workflows/opal.yml +0 -61
- data/.gitmodules +0 -6
- data/lib/compat/opal/moxml_boot.rb +0 -68
- data/lib/compat/opal/rexml/namespace.rb +0 -59
- data/lib/compat/opal/rexml/parsers/baseparser.rb +0 -1016
- data/lib/compat/opal/rexml/source.rb +0 -214
- data/lib/compat/opal/rexml/text.rb +0 -426
- data/lib/compat/opal/rexml/xmltokens.rb +0 -45
- data/lib/compat/opal/rexml_compat.rb +0 -77
- data/lib/moxml/entity_registry_opal_data.rb +0 -2138
- data/lib/moxml/native_attachment/opal.rb +0 -38
- data/spec/moxml/native_attachment/opal_spec.rb +0 -66
- data/spec/moxml/opal_oga_adapter_spec.rb +0 -20
- data/spec/moxml/opal_oga_features_spec.rb +0 -212
- data/spec/moxml/opal_oga_smoke_spec.rb +0 -35
- data/spec/moxml/opal_rexml_adapter_spec.rb +0 -14
- data/spec/moxml/opal_smoke_spec.rb +0 -61
- data/spec/moxml/reference_opal_spec.rb +0 -29
- data/spec/moxml/signature_autoload_spec.rb +0 -52
- data/spec/support/opal.rb +0 -16
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Moxml
|
|
4
|
-
class NativeAttachment
|
|
5
|
-
# Opal adapter nodes are Ruby objects, so instance variables are sufficient
|
|
6
|
-
# for Moxml-owned attachments without relying on Monitor/Thread support.
|
|
7
|
-
class Opal
|
|
8
|
-
def get(native, key)
|
|
9
|
-
native.instance_variable_get(attachment_ivar_name(key))
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def set(native, key, value)
|
|
13
|
-
native.instance_variable_set(attachment_ivar_name(key), value)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def none_set?(native, keys)
|
|
17
|
-
keys.none? { |key| native.instance_variable_defined?(attachment_ivar_name(key)) }
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def key?(native, key)
|
|
21
|
-
native.instance_variable_defined?(attachment_ivar_name(key))
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def delete(native, key)
|
|
25
|
-
ivar_name = attachment_ivar_name(key)
|
|
26
|
-
return unless native.instance_variable_defined?(ivar_name)
|
|
27
|
-
|
|
28
|
-
native.remove_instance_variable(ivar_name)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def attachment_ivar_name(key)
|
|
34
|
-
:"@moxml_attachment_#{key}"
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
RSpec.describe Moxml::NativeAttachment::Opal do
|
|
6
|
-
subject(:attachments) { described_class.new }
|
|
7
|
-
|
|
8
|
-
let(:native) { Object.new }
|
|
9
|
-
let(:other_native) { Object.new }
|
|
10
|
-
|
|
11
|
-
it "stores and reads attachments by native object and key" do
|
|
12
|
-
attachments.set(native, :entity_refs, ["amp"])
|
|
13
|
-
attachments.set(native, :doctype, "html")
|
|
14
|
-
attachments.set(other_native, :entity_refs, ["lt"])
|
|
15
|
-
|
|
16
|
-
aggregate_failures do
|
|
17
|
-
expect(attachments.get(native, :entity_refs)).to eq(["amp"])
|
|
18
|
-
expect(attachments.get(native, :doctype)).to eq("html")
|
|
19
|
-
expect(attachments.get(other_native, :entity_refs)).to eq(["lt"])
|
|
20
|
-
expect(attachments.key?(native, :entity_refs)).to be(true)
|
|
21
|
-
expect(attachments.key?(native, :missing)).to be(false)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "preserves explicit nil attachments" do
|
|
26
|
-
attachments.set(native, :xml_declaration, nil)
|
|
27
|
-
|
|
28
|
-
aggregate_failures do
|
|
29
|
-
expect(attachments.get(native, :xml_declaration)).to be_nil
|
|
30
|
-
expect(attachments.key?(native, :xml_declaration)).to be(true)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it "deletes attachments" do
|
|
35
|
-
attachments.set(native, :entity_refs, ["amp"])
|
|
36
|
-
|
|
37
|
-
expect(attachments.delete(native, :entity_refs)).to eq(["amp"])
|
|
38
|
-
|
|
39
|
-
aggregate_failures do
|
|
40
|
-
expect(attachments.get(native, :entity_refs)).to be_nil
|
|
41
|
-
expect(attachments.key?(native, :entity_refs)).to be(false)
|
|
42
|
-
expect(attachments.delete(native, :entity_refs)).to be_nil
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "stores attachments in Moxml-owned instance variables" do
|
|
47
|
-
attachments = described_class.new
|
|
48
|
-
native = Object.new
|
|
49
|
-
|
|
50
|
-
attachments.set(native, :entity_refs, ["amp"])
|
|
51
|
-
|
|
52
|
-
expect(native.instance_variable_get(:@moxml_attachment_entity_refs))
|
|
53
|
-
.to eq(["amp"])
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "removes the attachment instance variable on delete" do
|
|
57
|
-
attachments = described_class.new
|
|
58
|
-
native = Object.new
|
|
59
|
-
|
|
60
|
-
attachments.set(native, :entity_refs, ["amp"])
|
|
61
|
-
attachments.delete(native, :entity_refs)
|
|
62
|
-
|
|
63
|
-
expect(native.instance_variable_defined?(:@moxml_attachment_entity_refs))
|
|
64
|
-
.to be(false)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
require "moxml/adapter/shared_examples/adapter_contract"
|
|
5
|
-
|
|
6
|
-
# The describe resolves the adapter constant at load time — under
|
|
7
|
-
# MRI nothing loads the Opal compat shim, so the whole describe must
|
|
8
|
-
# sit behind the engine check (the `if:` metadata alone still
|
|
9
|
-
# evaluates the constant reference).
|
|
10
|
-
if RUBY_ENGINE == "opal"
|
|
11
|
-
RSpec.describe Moxml::Adapter::Oga do
|
|
12
|
-
around do |example|
|
|
13
|
-
Moxml.with_config(:oga, true, "UTF-8") do
|
|
14
|
-
example.run
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it_behaves_like "xml adapter"
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
# Exercises feature areas under Opal that are not covered by the shared
|
|
6
|
-
# adapter contract (opal_oga_adapter_spec.rb) or the basic smoke spec
|
|
7
|
-
# (opal_oga_smoke_spec.rb). Targets the default Opal adapter (:oga).
|
|
8
|
-
# Uses an explicit :oga context so leaked global config from other specs
|
|
9
|
-
# cannot flip the adapter under test.
|
|
10
|
-
RSpec.describe "Moxml Opal oga feature coverage", if: RUBY_ENGINE == "opal" do
|
|
11
|
-
let(:context) { Moxml.new(:oga) }
|
|
12
|
-
|
|
13
|
-
describe "builder DSL" do
|
|
14
|
-
it "builds a document with the block DSL" do
|
|
15
|
-
doc = Moxml::Builder.new(context).build do
|
|
16
|
-
root do
|
|
17
|
-
child "text"
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
expect(doc.root.name).to eq("root")
|
|
22
|
-
expect(doc.root.children.first.name).to eq("child")
|
|
23
|
-
expect(doc.root.children.first.text).to eq("text")
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it "creates nested elements via blocks" do
|
|
27
|
-
doc = Moxml::Builder.new(context).build do
|
|
28
|
-
library do
|
|
29
|
-
book(id: "1") { title "A" }
|
|
30
|
-
book(id: "2") { title "B" }
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
books = doc.root.children.grep(Moxml::Element)
|
|
35
|
-
expect(books.length).to eq(2)
|
|
36
|
-
expect(books.map { |b| b["id"] }).to eq(%w[1 2])
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it "supports method_missing DSL with attributes and text" do
|
|
40
|
-
doc = Moxml::Builder.new(context).build do
|
|
41
|
-
person(name: "Alice", age: "30") { email "alice@example.com" }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
person = doc.root
|
|
45
|
-
expect(person.name).to eq("person")
|
|
46
|
-
expect(person["name"]).to eq("Alice")
|
|
47
|
-
expect(person["age"]).to eq("30")
|
|
48
|
-
expect(person.children.first.name).to eq("email")
|
|
49
|
-
expect(person.children.first.text).to eq("alice@example.com")
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
it "strips trailing underscore for reserved-name tags" do
|
|
53
|
-
doc = Moxml::Builder.new(context).build do
|
|
54
|
-
class_ { name "Foo" }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
expect(doc.root.name).to eq("class")
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it "attaches namespace declarations" do
|
|
61
|
-
builder = Moxml::Builder.new(context)
|
|
62
|
-
doc = builder.build do
|
|
63
|
-
root("xmlns:dc": "http://purl.org/dc/elements/1.1/") do
|
|
64
|
-
builder.element("dc:title") { builder.text "Hello" }
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
title = doc.root.children.find { |c| c.is_a?(Moxml::Element) }
|
|
69
|
-
expect(title.name).to eq("dc:title")
|
|
70
|
-
expect(doc.to_xml).to include("xmlns:dc")
|
|
71
|
-
expect(doc.to_xml).to include("dc:title")
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "XML declaration preservation" do
|
|
76
|
-
it "does not add a declaration when the input has none" do
|
|
77
|
-
doc = context.parse("<root><child/></root>")
|
|
78
|
-
output = doc.to_xml
|
|
79
|
-
|
|
80
|
-
expect(output).not_to include("<?xml")
|
|
81
|
-
expect(doc.has_xml_declaration).to be false
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it "preserves the declaration when the input has one" do
|
|
85
|
-
xml = '<?xml version="1.0" encoding="UTF-8"?><root/>'
|
|
86
|
-
doc = context.parse(xml)
|
|
87
|
-
|
|
88
|
-
expect(doc.has_xml_declaration).to be true
|
|
89
|
-
expect(doc.to_xml).to include("<?xml")
|
|
90
|
-
expect(doc.to_xml).to include('version="1.0"')
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it "forces the declaration on with declaration: true" do
|
|
94
|
-
doc = context.parse("<root/>")
|
|
95
|
-
expect(doc.to_xml(declaration: true)).to include("<?xml")
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it "forces the declaration off with declaration: false" do
|
|
99
|
-
doc = context.parse('<?xml version="1.0"?><root/>')
|
|
100
|
-
expect(doc.to_xml(declaration: false)).not_to include("<?xml")
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
it "preserves standalone attribute" do
|
|
104
|
-
xml = '<?xml version="1.0" standalone="yes"?><root/>'
|
|
105
|
-
doc = context.parse(xml)
|
|
106
|
-
expect(doc.to_xml).to include("standalone")
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
describe "doctype handling" do
|
|
111
|
-
it "parses a SIMPLE doctype" do
|
|
112
|
-
doc = context.parse("<!DOCTYPE root><root/>")
|
|
113
|
-
doctype = doc.children.find { |c| c.is_a?(Moxml::Doctype) }
|
|
114
|
-
expect(doctype).not_to be_nil
|
|
115
|
-
expect(doctype.name).to eq("root")
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
it "parses a PUBLIC doctype with external and system ids" do
|
|
119
|
-
xml = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html/>'
|
|
120
|
-
doc = context.parse(xml)
|
|
121
|
-
doctype = doc.children.find { |c| c.is_a?(Moxml::Doctype) }
|
|
122
|
-
|
|
123
|
-
expect(doctype).not_to be_nil
|
|
124
|
-
expect(doctype.name).to eq("html")
|
|
125
|
-
expect(doctype.external_id).to eq("-//W3C//DTD HTML 4.01//EN")
|
|
126
|
-
expect(doctype.system_id).to eq("http://www.w3.org/TR/html4/strict.dtd")
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it "round-trips the doctype through serialization" do
|
|
130
|
-
xml = "<!DOCTYPE root><root/>"
|
|
131
|
-
doc = context.parse(xml)
|
|
132
|
-
expect(doc.to_xml).to include("DOCTYPE")
|
|
133
|
-
expect(doc.to_xml).to include("root")
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
describe "round-trip stability" do
|
|
138
|
-
it "round-trips a document with mixed content" do
|
|
139
|
-
xml = <<~XML.strip
|
|
140
|
-
<root attr="value">
|
|
141
|
-
<child>text</child>
|
|
142
|
-
<!-- comment -->
|
|
143
|
-
<nested><deep>data</deep></nested>
|
|
144
|
-
</root>
|
|
145
|
-
XML
|
|
146
|
-
|
|
147
|
-
doc1 = context.parse(xml)
|
|
148
|
-
serialized1 = doc1.to_xml
|
|
149
|
-
doc2 = context.parse(serialized1)
|
|
150
|
-
serialized2 = doc2.to_xml
|
|
151
|
-
|
|
152
|
-
expect(serialized2).to eq(serialized1)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
it "round-trips a document with namespaces" do
|
|
156
|
-
xml = '<root xmlns:ns="http://example.com/ns"><ns:child>content</ns:child></root>'
|
|
157
|
-
doc1 = context.parse(xml)
|
|
158
|
-
serialized1 = doc1.to_xml
|
|
159
|
-
|
|
160
|
-
doc2 = context.parse(serialized1)
|
|
161
|
-
child = doc2.root.children.find { |c| c.is_a?(Moxml::Element) }
|
|
162
|
-
expect(child.name).to eq("child")
|
|
163
|
-
expect(child.namespace_prefix).to eq("ns")
|
|
164
|
-
expect(doc2.to_xml).to include("xmlns:ns")
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
it "round-trips a document with CDATA" do
|
|
168
|
-
xml = "<root><![CDATA[<unparsed>content</unparsed>]]></root>"
|
|
169
|
-
doc1 = context.parse(xml)
|
|
170
|
-
serialized1 = doc1.to_xml
|
|
171
|
-
|
|
172
|
-
expect(serialized1).to include("<![CDATA[")
|
|
173
|
-
doc2 = context.parse(serialized1)
|
|
174
|
-
expect(doc2.to_xml).to include("<![CDATA[")
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
describe "XPath under Opal" do
|
|
179
|
-
it "evaluates element-only XPath" do
|
|
180
|
-
doc = context.parse("<root><a><b>1</b></a><a><b>2</b></a></root>")
|
|
181
|
-
results = doc.xpath("//a/b")
|
|
182
|
-
expect(results.length).to eq(2)
|
|
183
|
-
expect(results.map(&:text)).to eq(%w[1 2])
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
it "supports attribute predicates" do
|
|
187
|
-
doc = context.parse('<root><item id="x">1</item><item id="y">2</item></root>')
|
|
188
|
-
found = doc.xpath("//item").find { |i| i["id"] == "y" }
|
|
189
|
-
expect(found.text).to eq("2")
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
it "supports at_xpath" do
|
|
193
|
-
doc = context.parse("<root><a>1</a><a>2</a></root>")
|
|
194
|
-
first = doc.at_xpath("//a")
|
|
195
|
-
expect(first.text).to eq("1")
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
describe "comment and processing instruction handling" do
|
|
200
|
-
it "preserves comments through parse/serialize" do
|
|
201
|
-
xml = "<root><!-- my comment --></root>"
|
|
202
|
-
doc = context.parse(xml)
|
|
203
|
-
expect(doc.to_xml).to include("my comment")
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it "preserves processing instructions through parse/serialize" do
|
|
207
|
-
xml = "<?pi-target pi-data?><root/>"
|
|
208
|
-
doc = context.parse(xml)
|
|
209
|
-
expect(doc.to_xml).to include("pi-target")
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
# Verifies that the Opal default adapter (:oga, set in Moxml::Config)
|
|
6
|
-
# loads correctly via the Opal-compatible oga fork. The fork's
|
|
7
|
-
# lib/oga.rb dispatches `require 'liboga'` to `require 'oga/native/lexer'`
|
|
8
|
-
# under Opal, which resolves to the pure-Ruby lexer vendored at
|
|
9
|
-
# vendor/opal-oga/ext/pureruby/.
|
|
10
|
-
RSpec.describe "Moxml Opal oga default", if: RUBY_ENGINE == "opal" do
|
|
11
|
-
let(:context) { Moxml.new }
|
|
12
|
-
|
|
13
|
-
it "defaults to :oga under Opal" do
|
|
14
|
-
expect(Moxml::Config::OPAL_DEFAULT_ADAPTER).to eq(:oga)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it "parses XML through the oga adapter" do
|
|
18
|
-
doc = context.parse("<root><child>text</child></root>")
|
|
19
|
-
expect(doc.root.name).to eq("root")
|
|
20
|
-
expect(doc.root.children.first.name).to eq("child")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "decodes entities single-pass" do
|
|
24
|
-
doc = context.parse("<root>&#38;</root>")
|
|
25
|
-
expect(doc.root.text).to eq("&")
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "serializes back to XML" do
|
|
29
|
-
xml = '<person name="Alice"><age>30</age></person>'
|
|
30
|
-
doc = context.parse(xml)
|
|
31
|
-
serialized = doc.to_xml
|
|
32
|
-
expect(serialized).to include("person")
|
|
33
|
-
expect(serialized).to include("Alice")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
require "moxml/adapter/shared_examples/adapter_contract"
|
|
5
|
-
|
|
6
|
-
RSpec.describe Moxml::Adapter::Rexml, if: RUBY_ENGINE == "opal" do
|
|
7
|
-
around do |example|
|
|
8
|
-
Moxml.with_config(:rexml, true, "UTF-8") do
|
|
9
|
-
example.run
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
it_behaves_like "xml adapter"
|
|
14
|
-
end
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
RSpec.describe "Moxml Opal smoke test", if: RUBY_ENGINE == "opal" do
|
|
6
|
-
let(:context) { Moxml.new(:rexml) }
|
|
7
|
-
|
|
8
|
-
it "parses XML string to document" do
|
|
9
|
-
doc = context.parse("<root><child>text</child></root>")
|
|
10
|
-
expect(doc).not_to be_nil
|
|
11
|
-
root = doc.root
|
|
12
|
-
expect(root.name).to eq("root")
|
|
13
|
-
expect(root.children.first.name).to eq("child")
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
it "round-trips parse and serialize" do
|
|
17
|
-
xml = '<person name="Alice"><age>30</age></person>'
|
|
18
|
-
doc = context.parse(xml)
|
|
19
|
-
serialized = doc.to_xml
|
|
20
|
-
expect(serialized).to include("person")
|
|
21
|
-
expect(serialized).to include("Alice")
|
|
22
|
-
expect(serialized).to include("30")
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "builds XML document from scratch" do
|
|
26
|
-
doc = context.create_document
|
|
27
|
-
root = doc.create_element("root")
|
|
28
|
-
root["attr"] = "value"
|
|
29
|
-
text = doc.create_text("hello")
|
|
30
|
-
root.add_child(text)
|
|
31
|
-
doc.add_child(root)
|
|
32
|
-
|
|
33
|
-
expect(doc.to_xml).to include('attr="value"')
|
|
34
|
-
expect(doc.to_xml).to include("hello")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "handles namespaces" do
|
|
38
|
-
xml = '<root xmlns:ns="http://example.com"><ns:child>data</ns:child></root>'
|
|
39
|
-
doc = context.parse(xml)
|
|
40
|
-
expect(doc.to_xml).to include("ns:child")
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it "handles CDATA sections" do
|
|
44
|
-
xml = "<root><![CDATA[<script>alert('xss')</script>]]></root>"
|
|
45
|
-
doc = context.parse(xml)
|
|
46
|
-
expect(doc.to_xml).to include("<script>")
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it "handles comments" do
|
|
50
|
-
xml = "<root><!-- a comment --><child/></root>"
|
|
51
|
-
doc = context.parse(xml)
|
|
52
|
-
expect(doc.to_xml).to include("a comment")
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "handles processing instructions" do
|
|
56
|
-
xml = '<?xml version="1.0"?><?pi-target pi-data?><root/>'
|
|
57
|
-
doc = context.parse(xml)
|
|
58
|
-
serialized = doc.to_xml
|
|
59
|
-
expect(serialized).to include("pi-target")
|
|
60
|
-
end
|
|
61
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
# The engine-free lane (TODO.sota/03): the reference is pure Ruby and
|
|
6
|
-
# answers the model where no native engine exists. This spec proves it
|
|
7
|
-
# transpiles and runs under Opal.
|
|
8
|
-
RSpec.describe "Moxml::Reference under Opal", if: RUBY_ENGINE == "opal" do
|
|
9
|
-
it "parses, answers faces, and writes without a native engine" do
|
|
10
|
-
document = Moxml::Reference.parse(
|
|
11
|
-
'<r a="1"><b>t&u</b><![CDATA[c]]><?pi d?></r>',
|
|
12
|
-
)
|
|
13
|
-
expect(Moxml::Reference.element_names(document)).to eq(%w[r b])
|
|
14
|
-
expect(Moxml::Reference.child_element_names(document)).to eq([["b"], []])
|
|
15
|
-
element = document.root.children.find(&:element?)
|
|
16
|
-
expect(element.children.map(&:kind)).to eq([:text])
|
|
17
|
-
expect(element.children.first.data).to eq("t&u")
|
|
18
|
-
expect(document.root.text).to eq("t&uc")
|
|
19
|
-
expect(Moxml::Reference.processing_instructions(document))
|
|
20
|
-
.to eq([["pi", "d"]])
|
|
21
|
-
expect(Moxml::Reference.to_xml(document))
|
|
22
|
-
.to include("<r a=\"1\">", "<b>t&u</b>", "<![CDATA[c]]>")
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "raises a named ParseError for malformed input" do
|
|
26
|
-
expect { Moxml::Reference.parse("<r><c></r>") }
|
|
27
|
-
.to raise_error(Moxml::ParseError)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
require "open3"
|
|
5
|
-
require "rbconfig"
|
|
6
|
-
require "tempfile"
|
|
7
|
-
require "opal"
|
|
8
|
-
|
|
9
|
-
RSpec.describe "Signature autoload boundary", :native_fs do
|
|
10
|
-
let(:lib) { File.expand_path("../../lib", __dir__) }
|
|
11
|
-
|
|
12
|
-
it "keeps the native signature module lazy and usable on Ruby" do
|
|
13
|
-
source = <<~RUBY_CODE
|
|
14
|
-
require "moxml"
|
|
15
|
-
abort "signature was loaded eagerly" if $LOADED_FEATURES.any? { |path| path.end_with?("moxml/signature.rb") }
|
|
16
|
-
abort "signature autoload missing" unless Moxml.autoload?(:Signature) == "moxml/signature"
|
|
17
|
-
abort "signature module unavailable" unless Moxml::Signature::DSIG_NS == "http://www.w3.org/2000/09/xmldsig#"
|
|
18
|
-
abort "OpenSSL unavailable" unless defined?(OpenSSL::PKey::RSA)
|
|
19
|
-
puts "native signature available"
|
|
20
|
-
RUBY_CODE
|
|
21
|
-
output, error, status = Open3.capture3(RbConfig.ruby, "-I", lib, "-e", source)
|
|
22
|
-
expect(status.success?).to be(true), error
|
|
23
|
-
expect(output).to include("native signature available")
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it "does not compile the native OpenSSL dependency into Opal" do
|
|
27
|
-
# Compile the actual entrypoint declaration. This isolates the optional
|
|
28
|
-
# signature boundary from the other modules loaded by the Opal boot file.
|
|
29
|
-
declaration = File.readlines(File.join(lib, "moxml.rb"))
|
|
30
|
-
.find { |line| line.include?('autoload :Signature, "moxml/signature"') }
|
|
31
|
-
expect(declaration).not_to be_nil
|
|
32
|
-
|
|
33
|
-
builder = Opal::Builder.new
|
|
34
|
-
builder.append_paths(lib)
|
|
35
|
-
builder.build("opal")
|
|
36
|
-
builder.build_str(<<~RUBY_CODE, "signature-autoload-boundary")
|
|
37
|
-
module Moxml
|
|
38
|
-
#{declaration}
|
|
39
|
-
end
|
|
40
|
-
raise "native Signature loaded under Opal" if Moxml.const_defined?(:Signature, false)
|
|
41
|
-
puts "Opal signature boundary passed"
|
|
42
|
-
RUBY_CODE
|
|
43
|
-
|
|
44
|
-
Tempfile.create(["moxml-signature-boundary", ".js"]) do |file|
|
|
45
|
-
file.write(builder.to_s)
|
|
46
|
-
file.flush
|
|
47
|
-
output, error, status = Open3.capture3("node", file.path)
|
|
48
|
-
expect(status.success?).to be(true), error
|
|
49
|
-
expect(output).to include("Opal signature boundary passed")
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
data/spec/support/opal.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Opal runtime patches for moxml specs.
|
|
4
|
-
#
|
|
5
|
-
# Moxml already handles Opal-specific behavior internally
|
|
6
|
-
# (NativeAttachment::Opal, Config::OPAL_DEFAULT_ADAPTER, EntityRegistry::OPAL_ENTITY_DATA).
|
|
7
|
-
# This file is loaded only under Opal for any additional test-environment patches.
|
|
8
|
-
|
|
9
|
-
# Under Opal, moxml uses REXML adapter (Opal reimplements strscan/stringio
|
|
10
|
-
# in its stdlib, enabling REXML to compile to JavaScript).
|
|
11
|
-
Moxml.configure do |config|
|
|
12
|
-
config.adapter = :rexml
|
|
13
|
-
config.strict_parsing = false
|
|
14
|
-
config.default_encoding = "UTF-8"
|
|
15
|
-
config.entity_load_mode = :optional
|
|
16
|
-
end
|