moxml 0.5.146 → 0.5.147
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/rake.yml +35 -0
- data/README.adoc +19 -1
- data/docs/_pages/adapter-compatibility.adoc +7 -0
- data/docs/_pages/divergences.adoc +90 -0
- data/docs/_references/index.adoc +9 -0
- data/docs/_references/processing-model.adoc +211 -0
- data/docs/index.adoc +9 -0
- data/lib/moxml/adapter/leptris.rb +30 -2
- data/lib/moxml/adapter/libxml.rb +5 -0
- data/lib/moxml/adapter/oga.rb +10 -0
- data/lib/moxml/document.rb +9 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/consistency/divergence_register_spec.rb +109 -0
- data/spec/consistency/document_text_spec.rb +30 -0
- data/spec/consistency/eol_normalization_spec.rb +8 -8
- data/spec/consistency/loud_degradation_spec.rb +44 -0
- data/spec/consistency/model_coverage_audit_spec.rb +113 -0
- data/spec/consistency/spec_constant_uniqueness_spec.rb +29 -0
- data/spec/consistency/xpath_idioms_spec.rb +1 -1
- data/spec/moxml/adapter/leptris_detached_text_spec.rb +34 -0
- data/spec/moxml/adapter/leptris_parse_lint_spec.rb +35 -0
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +3 -3
- data/spec/moxml/c14n/adapter_delegation_spec.rb +3 -3
- data/spec/moxml/c14n/comments_pis_spec.rb +2 -2
- data/spec/moxml/c14n/subset_spec.rb +4 -4
- data/spec/moxml/c14n/w3c_examples_spec.rb +3 -1
- data/spec/moxml/declaration_preservation_spec.rb +2 -2
- data/spec/moxml/element_add_element_spec.rb +3 -3
- data/spec/moxml/element_attribute_pairs_spec.rb +2 -2
- data/spec/moxml/node_spec.rb +1 -1
- data/spec/moxml/plan_conformance_spec.rb +3 -3
- data/spec/moxml/sax_entity_parity_spec.rb +3 -3
- data/spec/moxml/sax_spec.rb +7 -7
- data/spec/moxml/struct_plan_spec.rb +2 -2
- data/spec/moxml/xpath_capabilities_spec.rb +13 -13
- metadata +10 -1
|
@@ -20,7 +20,7 @@ require "spec_helper"
|
|
|
20
20
|
# binding release vending that engine rides
|
|
21
21
|
# (gate: binding 1.9.240).
|
|
22
22
|
RSpec.describe "XML line-ending normalization" do
|
|
23
|
-
|
|
23
|
+
EOL_NORMALIZATION_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
24
24
|
|
|
25
25
|
def with_adapter(name)
|
|
26
26
|
ctx = Moxml.new(name)
|
|
@@ -37,10 +37,10 @@ RSpec.describe "XML line-ending normalization" do
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
it "normalizes CRLF and lone CR to LF in text content" do
|
|
40
|
-
|
|
40
|
+
EOL_NORMALIZATION_ADAPTERS.each do |name|
|
|
41
41
|
next unless leptris_fixed?(name)
|
|
42
42
|
|
|
43
|
-
skip "oga preserves raw CR (upstream)" if name == :oga
|
|
43
|
+
skip "D-02: oga preserves raw CR (upstream)" if name == :oga
|
|
44
44
|
|
|
45
45
|
with_adapter(name) do |ctx|
|
|
46
46
|
text = ctx.parse("<r>a\r\nb\rc</r>").root.children.first
|
|
@@ -50,10 +50,10 @@ RSpec.describe "XML line-ending normalization" do
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
it "normalizes EOL inside CDATA content" do
|
|
53
|
-
|
|
53
|
+
EOL_NORMALIZATION_ADAPTERS.each do |name|
|
|
54
54
|
next unless leptris_fixed?(name)
|
|
55
55
|
|
|
56
|
-
skip "oga preserves raw CR (upstream)" if name == :oga
|
|
56
|
+
skip "D-02: oga preserves raw CR (upstream)" if name == :oga
|
|
57
57
|
|
|
58
58
|
with_adapter(name) do |ctx|
|
|
59
59
|
cdata = ctx.parse("<r><![CDATA[a\r\nb]]></r>").root.children.first
|
|
@@ -63,11 +63,11 @@ RSpec.describe "XML line-ending normalization" do
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
it "preserves CR from character references" do
|
|
66
|
-
|
|
66
|
+
EOL_NORMALIZATION_ADAPTERS.each do |name|
|
|
67
67
|
next unless leptris_fixed?(name)
|
|
68
68
|
|
|
69
|
-
skip "ox collapses to LF (upstream)" if name == :ox
|
|
70
|
-
skip "oga preserves raw CR (upstream)" if name == :oga
|
|
69
|
+
skip "D-03: ox collapses to LF (upstream)" if name == :ox
|
|
70
|
+
skip "D-02: oga preserves raw CR (upstream)" if name == :oga
|
|
71
71
|
|
|
72
72
|
with_adapter(name) do |ctx|
|
|
73
73
|
text = ctx.parse("<r>a b</r>").root.children.first
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Loud degradation (processing model, "Errors and loud degradation"):
|
|
6
|
+
# a capability an engine cannot deliver must answer the contract or
|
|
7
|
+
# raise the named error — never a divergent answer. Probed across all
|
|
8
|
+
# available adapters; the capability lists mirror the adapters'
|
|
9
|
+
# actual engine backing.
|
|
10
|
+
RSpec.describe "loud capability contracts" do
|
|
11
|
+
available = Moxml::Adapter::AVAILABLE_ADAPTERS
|
|
12
|
+
xslt_capable = %i[nokogiri leptris] & available
|
|
13
|
+
html_capable = %i[nokogiri leptris] & available
|
|
14
|
+
|
|
15
|
+
let(:sheet) do
|
|
16
|
+
%(<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">) \
|
|
17
|
+
"<xsl:template match='/'><o/></xsl:template></xsl:stylesheet>"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
(available - xslt_capable).each do |name|
|
|
21
|
+
it "raises NotImplementedError for XSLT on #{name}" do
|
|
22
|
+
ctx = Moxml.new(name)
|
|
23
|
+
expect { ctx.xslt(sheet) }.to raise_error(Moxml::NotImplementedError)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
(available - html_capable).each do |name|
|
|
28
|
+
it "raises NotImplementedError for to_xhtml on #{name}" do
|
|
29
|
+
doc = Moxml.new(name).parse("<r><a/></r>")
|
|
30
|
+
expect { doc.to_xhtml }.to raise_error(Moxml::NotImplementedError)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "raises AdapterError for to_html on #{name}" do
|
|
34
|
+
doc = Moxml.new(name).parse("<r><a/></r>")
|
|
35
|
+
expect { doc.to_html }.to raise_error(Moxml::AdapterError)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
xslt_capable.each do |name|
|
|
40
|
+
it "compiles a stylesheet on #{name}" do
|
|
41
|
+
expect(Moxml.new(name).xslt(sheet)).to be_a(Moxml::XSLT::Stylesheet)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Model coverage matrix (TODO.model/03): every face family of the
|
|
6
|
+
# processing model is governed for every adapter — covered by a pin
|
|
7
|
+
# (the consistency/contract spec that asserts equivalence), exempt via
|
|
8
|
+
# a registered divergence (adapter => D-ID), or loud (adapter =>
|
|
9
|
+
# the named error class). An unpinned family is an ungoverned cell:
|
|
10
|
+
# this audit fails until it has one of the three.
|
|
11
|
+
# See docs/_references/processing-model.adoc for the model itself.
|
|
12
|
+
RSpec.describe "model coverage matrix" do
|
|
13
|
+
MODEL_MATRIX = { # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
14
|
+
"document order and children listing" => {
|
|
15
|
+
pin: "spec/consistency/adapter_parity_spec.rb",
|
|
16
|
+
},
|
|
17
|
+
"name resolution" => {
|
|
18
|
+
pin: "spec/consistency/xpath_idioms_spec.rb",
|
|
19
|
+
},
|
|
20
|
+
"namespace resolution and bindings normalization" => {
|
|
21
|
+
pin: "spec/consistency/xpath_idioms_spec.rb",
|
|
22
|
+
},
|
|
23
|
+
"attribute access" => {
|
|
24
|
+
pin: "spec/moxml/attribute_resolver_spec.rb",
|
|
25
|
+
},
|
|
26
|
+
"text/content/raw_content" => {
|
|
27
|
+
pin: "spec/moxml/text_spec.rb",
|
|
28
|
+
},
|
|
29
|
+
"parsing modes and diagnostics" => {
|
|
30
|
+
pin: "spec/moxml/document_parse_diagnostics_spec.rb",
|
|
31
|
+
},
|
|
32
|
+
"EOL normalization" => {
|
|
33
|
+
pin: "spec/consistency/eol_normalization_spec.rb",
|
|
34
|
+
exempt: { oga: "D-02", ox: "D-03" },
|
|
35
|
+
},
|
|
36
|
+
"entity modes" => {
|
|
37
|
+
pin: "spec/moxml/entity_spec.rb",
|
|
38
|
+
},
|
|
39
|
+
"XPath evaluation and declared idioms" => {
|
|
40
|
+
pin: "spec/consistency/xpath_idioms_spec.rb",
|
|
41
|
+
exempt: { oga: "D-01" },
|
|
42
|
+
},
|
|
43
|
+
"serialization shapes (xml/html/xhtml)" => {
|
|
44
|
+
pin: "spec/moxml/xhtml_output_spec.rb",
|
|
45
|
+
},
|
|
46
|
+
"indentation knobs" => {
|
|
47
|
+
pin: "spec/moxml/to_html_spec.rb",
|
|
48
|
+
exempt: { oga: "D-09", ox: "D-09", headed_ox: "D-09",
|
|
49
|
+
libxml: "D-09", leptris: "D-09" },
|
|
50
|
+
},
|
|
51
|
+
"canonicalization" => {
|
|
52
|
+
pin: "spec/moxml/c14n/adapter_delegation_spec.rb",
|
|
53
|
+
exempt: { oga: "D-04", ox: "D-05", headed_ox: "D-05" },
|
|
54
|
+
},
|
|
55
|
+
"XSLT" => {
|
|
56
|
+
pin: "spec/consistency/xslt_spec.rb",
|
|
57
|
+
loud: { oga: "Moxml::NotImplementedError",
|
|
58
|
+
ox: "Moxml::NotImplementedError",
|
|
59
|
+
headed_ox: "Moxml::NotImplementedError",
|
|
60
|
+
rexml: "Moxml::NotImplementedError",
|
|
61
|
+
libxml: "Moxml::NotImplementedError" },
|
|
62
|
+
},
|
|
63
|
+
"SAX" => {
|
|
64
|
+
pin: "spec/moxml/sax_spec.rb",
|
|
65
|
+
},
|
|
66
|
+
"materialization" => {
|
|
67
|
+
pin: "spec/moxml/plan_conformance_spec.rb",
|
|
68
|
+
},
|
|
69
|
+
"loud capability contracts" => {
|
|
70
|
+
pin: "spec/consistency/loud_degradation_spec.rb",
|
|
71
|
+
},
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
def register_ids
|
|
75
|
+
File.readlines("docs/_pages/divergences.adoc")
|
|
76
|
+
.filter_map { |line| line[/\A\|\s*(D-\d+)\s*$/, 1] }
|
|
77
|
+
.uniq
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "governs every face family with a pin" do
|
|
81
|
+
unpinned = MODEL_MATRIX.reject { |_, cell| cell[:pin] }.keys
|
|
82
|
+
expect(unpinned).to be_empty,
|
|
83
|
+
"ungoverned face families (no pin): " \
|
|
84
|
+
"#{unpinned.join(', ')}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "points every pin at an existing spec" do
|
|
88
|
+
missing = MODEL_MATRIX.filter_map do |family, cell|
|
|
89
|
+
family unless cell[:pin] && File.exist?(cell[:pin])
|
|
90
|
+
end
|
|
91
|
+
expect(missing).to be_empty, "pins missing on disk: #{missing.join(', ')}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "exempts adapters only via registered divergences" do
|
|
95
|
+
unknown = MODEL_MATRIX.flat_map do |family, cell|
|
|
96
|
+
(cell[:exempt] || {}).filter_map do |adapter, id|
|
|
97
|
+
"#{family}: #{adapter} -> #{id}" unless register_ids.include?(id)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
expect(unknown).to be_empty,
|
|
101
|
+
"exemptions cite unregistered IDs: #{unknown.join('; ')}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "declares loud cells only with named Moxml errors" do
|
|
105
|
+
bad = MODEL_MATRIX.flat_map do |family, cell|
|
|
106
|
+
(cell[:loud] || {}).filter_map do |adapter, error|
|
|
107
|
+
"#{family}: #{adapter} -> #{error}" unless error.start_with?("Moxml::")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
expect(bad).to be_empty,
|
|
111
|
+
"loud cells must name Moxml errors: #{bad.join('; ')}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Checker integrity (TODO.model/06): top-level constant names in spec
|
|
6
|
+
# files all resolve through Object inside example blocks, so the same
|
|
7
|
+
# name defined by two files makes the last-loaded file win — the 0.5.143
|
|
8
|
+
# CORPUS collision made the battery fail ~50% of runs with a foreign
|
|
9
|
+
# corpus. This guard kills the class.
|
|
10
|
+
RSpec.describe "spec-file top-level constant uniqueness" do
|
|
11
|
+
it "defines each constant name in at most one spec/support file" do
|
|
12
|
+
seen = {}
|
|
13
|
+
|
|
14
|
+
Dir["spec/**/*.rb"].each do |path|
|
|
15
|
+
File.readlines(path).each_with_index do |line, i|
|
|
16
|
+
match = line.match(/\A\s*([A-Z][A-Z0-9_]*)\s*=[^=]/)
|
|
17
|
+
next unless match
|
|
18
|
+
|
|
19
|
+
name = match[1]
|
|
20
|
+
expect(seen)
|
|
21
|
+
.not_to have_key(name),
|
|
22
|
+
"#{name} defined at #{seen[name]} and #{path}:#{i + 1} " \
|
|
23
|
+
"— constants inside example blocks collide through " \
|
|
24
|
+
"Object; give one file a unique name"
|
|
25
|
+
seen[name] = "#{path}:#{i + 1}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -42,7 +42,7 @@ RSpec.describe "XPath idioms across adapters" do
|
|
|
42
42
|
if adapter_name == :oga
|
|
43
43
|
# oga's own Ruby XPath engine has no xmlns: default-ns
|
|
44
44
|
# support (third-party; explicit bindings work).
|
|
45
|
-
skip "oga's XPath engine does not implement the xmlns: idiom"
|
|
45
|
+
skip "D-01: oga's XPath engine does not implement the xmlns: idiom"
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Issue #401: Text#content= on a detached (parentless) node answered
|
|
6
|
+
# the engine's bare "Invalid argument" with no indication of the cause.
|
|
7
|
+
# The leptris adapter now re-raises it as a named Moxml error carrying
|
|
8
|
+
# the detachment. Created-unattached nodes work (probe), so only the
|
|
9
|
+
# failing detachment is re-raised.
|
|
10
|
+
RSpec.describe "detached text content= names the cause" do
|
|
11
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
12
|
+
|
|
13
|
+
it "raises a named Moxml error for a removed text node" do
|
|
14
|
+
doc = ctx.parse("<r><a>x</a><b>y</b></r>")
|
|
15
|
+
text = doc.root.children[0].children[0]
|
|
16
|
+
text.remove
|
|
17
|
+
|
|
18
|
+
expect { text.content = "z" }
|
|
19
|
+
.to raise_error(Moxml::Error, /detached \(parentless\).*issue #401/)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "keeps created-unattached nodes mutable" do
|
|
23
|
+
text = ctx.create_document.create_text("x")
|
|
24
|
+
text.content = "y"
|
|
25
|
+
expect(text.content).to eq("y")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "keeps attached nodes mutable" do
|
|
29
|
+
doc = ctx.parse("<r><a>x</a></r>")
|
|
30
|
+
text = doc.root.children[0].children[0]
|
|
31
|
+
text.content = "z"
|
|
32
|
+
expect(text.content).to eq("z")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# Issue #404: Context#parse_html forwards collect_errors (and mode) to
|
|
6
|
+
# the leptris flags entry (leptris#1771's §13.2.5 parse-lint) — the
|
|
7
|
+
# recorded error tokens surface through parse_diagnostics. Face-probed:
|
|
8
|
+
# bindings predating the kwarg skip.
|
|
9
|
+
RSpec.describe "parse_html collect_errors forwarding" do
|
|
10
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
11
|
+
let(:src) { %(<p class="x>unclosed</p><a href=><p>a<b</p>) }
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
adapter = ctx.config.adapter
|
|
15
|
+
supported = adapter.singleton_class.const_get(:COLLECT_ERRORS_NATIVE)
|
|
16
|
+
skip "binding lacks the collect_errors kwarg (leptris#1771)" unless supported
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "surfaces parse-lint errors through parse_diagnostics" do
|
|
20
|
+
doc = ctx.parse_html(src, collect_errors: true)
|
|
21
|
+
diags = doc.parse_diagnostics
|
|
22
|
+
expect(diags).not_to be_empty
|
|
23
|
+
expect(diags.first).to have_key(:message)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "keeps the default lane silent (no option, no diagnostics)" do
|
|
27
|
+
doc = ctx.parse_html(src)
|
|
28
|
+
expect(doc.parse_diagnostics).to be_empty
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "forwards through Context#parse_html options" do
|
|
32
|
+
doc = ctx.parse_html(src, { collect_errors: true })
|
|
33
|
+
expect(doc.parse_diagnostics).not_to be_empty
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -343,13 +343,13 @@ RSpec.shared_examples "xml adapter" do
|
|
|
343
343
|
it "respects indentation settings" do
|
|
344
344
|
if described_class.name.include?("Oga") &&
|
|
345
345
|
!described_class.indents_output?
|
|
346
|
-
pending("Oga does not support indentation settings")
|
|
346
|
+
pending("D-09: Oga does not support indentation settings")
|
|
347
347
|
end
|
|
348
348
|
if described_class.name.include?("Libxml")
|
|
349
|
-
skip("LibXML serialization does not support indentation (documented limitation)")
|
|
349
|
+
skip("D-09: LibXML serialization does not support indentation (documented limitation)")
|
|
350
350
|
end
|
|
351
351
|
if described_class.name.include?("Ox")
|
|
352
|
-
skip("Ox does not support indentation settings (documented limitation)")
|
|
352
|
+
skip("D-09: Ox does not support indentation settings (documented limitation)")
|
|
353
353
|
end
|
|
354
354
|
|
|
355
355
|
unindented = described_class.serialize(doc, indent: 0)
|
|
@@ -58,9 +58,9 @@ RSpec.describe "Moxml::C14n cross-adapter canonicalization" do
|
|
|
58
58
|
|
|
59
59
|
describe "document-level nodes keep document order" do
|
|
60
60
|
# oga surfaces the XML declaration as a <?xml?> PI and keeps the
|
|
61
|
-
# separator space in PI content; ox/headed_ox drop PI
|
|
62
|
-
# parse. Both are pre-existing parse-layer
|
|
63
|
-
# scope here.
|
|
61
|
+
# separator space in PI content (D-04); ox/headed_ox drop PI
|
|
62
|
+
# content on parse (D-05). Both are pre-existing parse-layer
|
|
63
|
+
# divergences, out of scope here.
|
|
64
64
|
let(:adapters) { Moxml::Adapter::AVAILABLE_ADAPTERS - %i[oga ox headed_ox] }
|
|
65
65
|
|
|
66
66
|
it "canonicalizes a leading PI before the root element" do
|
|
@@ -53,13 +53,13 @@ RSpec.describe "C14N comments and PIs" do
|
|
|
53
53
|
# PIs, and the DataModel.from_document path drops them. Fixing requires
|
|
54
54
|
# adapter-level work and is tracked separately.
|
|
55
55
|
it "renders PIs outside the document element (KNOWN LIMITATION)" do
|
|
56
|
-
skip "document-level PI rendering not yet implemented"
|
|
56
|
+
skip "D-08: document-level PI rendering not yet implemented"
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
describe "comments at document level" do
|
|
61
61
|
it "renders document-level comments when with_comments: true (KNOWN LIMITATION)" do
|
|
62
|
-
skip "document-level comment rendering not yet implemented"
|
|
62
|
+
skip "D-08: document-level comment rendering not yet implemented"
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -63,7 +63,7 @@ RSpec.describe "Moxml::C14n subset canonicalization" do
|
|
|
63
63
|
next if label == "text selection" && adapter_name == :rexml
|
|
64
64
|
|
|
65
65
|
# rexml's adapter xpath does not return text nodes for
|
|
66
|
-
# text() node tests (pre-existing capability gap).
|
|
66
|
+
# text() node tests (D-07, pre-existing capability gap).
|
|
67
67
|
|
|
68
68
|
it "canonicalizes #{label}" do
|
|
69
69
|
doc = ctx.parse(xml)
|
|
@@ -72,9 +72,9 @@ RSpec.describe "Moxml::C14n subset canonicalization" do
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
# ox/headed_ox strip comment content padding at the parse
|
|
75
|
-
# layer (pre-existing, same family as their PI content
|
|
76
|
-
# only the rendered-comment case depends on it. rexml's
|
|
77
|
-
# comment() selection gap also only affects this case.
|
|
75
|
+
# layer (D-06, pre-existing, same family as their PI content
|
|
76
|
+
# loss); only the rendered-comment case depends on it. rexml's
|
|
77
|
+
# comment() selection gap (D-07) also only affects this case.
|
|
78
78
|
unless COMMENTS_PADDING_LOSS.include?(adapter_name) ||
|
|
79
79
|
COMMENT_SELECTION_GAP.include?(adapter_name)
|
|
80
80
|
it COMMENTS_CASE[0] do
|
|
@@ -55,7 +55,8 @@ RSpec.describe "Moxml::C14n W3C REC-xml-c14n examples" do
|
|
|
55
55
|
|
|
56
56
|
W3C_FULL_FORMS.each_with_index do |(label, xml, expected), idx|
|
|
57
57
|
# ox/headed_ox strip inter-element whitespace, expand 
|
|
58
|
-
# and pad comments at parse; oga keeps the PI
|
|
58
|
+
# and pad comments at parse (D-05, D-06); oga keeps the PI
|
|
59
|
+
# separator space (D-04).
|
|
59
60
|
skip_shapes = { ox: [0, CR_SHAPE, PI_SHAPE],
|
|
60
61
|
headed_ox: [0, CR_SHAPE, PI_SHAPE],
|
|
61
62
|
oga: [PI_SHAPE] }[adapter_name] || []
|
|
@@ -72,6 +73,7 @@ RSpec.describe "Moxml::C14n W3C REC-xml-c14n examples" do
|
|
|
72
73
|
|
|
73
74
|
describe "W3C §3 document-subset whitespace" do
|
|
74
75
|
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |adapter_name|
|
|
76
|
+
# ox/headed_ox strip inter-element whitespace at parse (D-06)
|
|
75
77
|
next if WHITESPACE_SHAPES.include?(adapter_name)
|
|
76
78
|
|
|
77
79
|
it "renders selected nodes only, whitespace runs in place (#{adapter_name})" do
|
|
@@ -4,9 +4,9 @@ require "spec_helper"
|
|
|
4
4
|
|
|
5
5
|
RSpec.describe "XML Declaration Preservation" do
|
|
6
6
|
# Test with all available adapters
|
|
7
|
-
|
|
7
|
+
DECLARATION_SPECS_ADAPTERS = %i[nokogiri oga rexml ox libxml headed_ox].freeze
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
DECLARATION_SPECS_ADAPTERS.each do |adapter_name|
|
|
10
10
|
context "with #{adapter_name} adapter" do
|
|
11
11
|
let(:context) { Moxml.new(adapter_name) }
|
|
12
12
|
|
|
@@ -9,7 +9,7 @@ require "spec_helper"
|
|
|
9
9
|
# the contract: a missing base default NoMethodErrors on non-face
|
|
10
10
|
# adapters, a broken fallback loses attributes or the attach.
|
|
11
11
|
RSpec.describe Moxml::Element do
|
|
12
|
-
|
|
12
|
+
ELEMENT_ADD_ELEMENT_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
13
13
|
|
|
14
14
|
def with_adapter(name)
|
|
15
15
|
ctx = Moxml.new(name)
|
|
@@ -19,7 +19,7 @@ RSpec.describe Moxml::Element do
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
it "attaches a child with all attributes on every available adapter" do
|
|
22
|
-
|
|
22
|
+
ELEMENT_ADD_ELEMENT_ADAPTERS.each do |name|
|
|
23
23
|
with_adapter(name) do |ctx|
|
|
24
24
|
doc = ctx.parse("<catalog/>")
|
|
25
25
|
root = doc.root
|
|
@@ -35,7 +35,7 @@ RSpec.describe Moxml::Element do
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it "nests under a non-root element with empty attrs" do
|
|
38
|
-
|
|
38
|
+
ELEMENT_ADD_ELEMENT_ADAPTERS.each do |name|
|
|
39
39
|
with_adapter(name) do |ctx|
|
|
40
40
|
doc = ctx.parse("<catalog><record/></catalog>")
|
|
41
41
|
record = doc.root.children.first
|
|
@@ -12,7 +12,7 @@ RSpec.describe Moxml::Element do
|
|
|
12
12
|
'<catalog><record id="r0" kind="k0"><field name="f0" unit="u0">v</field></record></catalog>'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
ELEMENT_ATTRIBUTE_PAIRS_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
16
16
|
|
|
17
17
|
def with_adapter(name)
|
|
18
18
|
ctx = Moxml.new(name)
|
|
@@ -22,7 +22,7 @@ RSpec.describe Moxml::Element do
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
it "matches the node path on every available adapter" do
|
|
25
|
-
results =
|
|
25
|
+
results = ELEMENT_ATTRIBUTE_PAIRS_ADAPTERS.filter_map do |name|
|
|
26
26
|
with_adapter(name) do |ctx|
|
|
27
27
|
root = ctx.parse(xml).root
|
|
28
28
|
[name, root.attribute_pairs]
|
data/spec/moxml/node_spec.rb
CHANGED
|
@@ -157,7 +157,7 @@ RSpec.describe Moxml::Node do
|
|
|
157
157
|
|
|
158
158
|
it "locates the node via its path" do
|
|
159
159
|
# Ox translates only a limited XPath subset (no positional predicates)
|
|
160
|
-
skip "ox xpath translation does not support positional predicates" if adapter_name == :ox
|
|
160
|
+
skip "D-12: ox xpath translation does not support positional predicates" if adapter_name == :ox
|
|
161
161
|
|
|
162
162
|
expect(doc.at_xpath(title.path)).to eq(title)
|
|
163
163
|
end
|
|
@@ -27,10 +27,10 @@ RSpec.describe "Moxml::Plan adapter conformance" do
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
PLAN_CONFORMANCE_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
31
31
|
|
|
32
32
|
it "executes on every available adapter" do
|
|
33
|
-
available =
|
|
33
|
+
available = PLAN_CONFORMANCE_ADAPTERS.select do |a|
|
|
34
34
|
ctx = Moxml.new(a)
|
|
35
35
|
ctx.parse(xml)
|
|
36
36
|
true
|
|
@@ -63,7 +63,7 @@ RSpec.describe "Moxml::Plan adapter conformance" do
|
|
|
63
63
|
on("field") { |attrs, text, _| [attrs["name"], text] }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
PLAN_CONFORMANCE_ADAPTERS.each do |a|
|
|
67
67
|
begin
|
|
68
68
|
ctx = Moxml.new(a)
|
|
69
69
|
ctx.parse(xml)
|
|
@@ -32,7 +32,7 @@ module SaxEntityParityFixtures
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
SAX_ENTITY_PARITY_ADAPTERS = %i[nokogiri ox oga rexml libxml headed_ox].freeze
|
|
36
36
|
|
|
37
37
|
# Each row: [input XML, decoded attribute value, expected attribute
|
|
38
38
|
# value as it appears in the serialized XML]. The serialized form
|
|
@@ -85,7 +85,7 @@ module SaxEntityParityFixtures
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
RSpec.describe "SAX/DOM entity parity" do
|
|
88
|
-
SaxEntityParityFixtures::
|
|
88
|
+
SaxEntityParityFixtures::SAX_ENTITY_PARITY_ADAPTERS.each do |adapter|
|
|
89
89
|
context "with #{adapter} adapter" do
|
|
90
90
|
let(:ctx) { Moxml.new(adapter) }
|
|
91
91
|
|
|
@@ -349,7 +349,7 @@ RSpec.describe "SAX/DOM entity parity" do
|
|
|
349
349
|
|
|
350
350
|
SaxEntityParityFixtures::ATTRIBUTE_CASES.each do |label, (xml, _expected, expected_serialized)|
|
|
351
351
|
it "every adapter emits the same attribute form: #{label}" do
|
|
352
|
-
results = SaxEntityParityFixtures::
|
|
352
|
+
results = SaxEntityParityFixtures::SAX_ENTITY_PARITY_ADAPTERS.to_h { |a| [a, serialize_via(a, xml)] }
|
|
353
353
|
results.each do |a, out|
|
|
354
354
|
expect(out).to(
|
|
355
355
|
include(%(x="#{expected_serialized}")),
|
data/spec/moxml/sax_spec.rb
CHANGED
|
@@ -413,7 +413,7 @@ RSpec.describe Moxml::SAX do
|
|
|
413
413
|
it "documents CDATA limitation" do
|
|
414
414
|
# Ox SAX does not have separate CDATA events
|
|
415
415
|
# All CDATA content is delivered as text() events
|
|
416
|
-
skip "Ox SAX does not support separate CDATA events"
|
|
416
|
+
skip "D-10: Ox SAX does not support separate CDATA events"
|
|
417
417
|
|
|
418
418
|
xml_with_cdata = "<root><![CDATA[special content]]></root>"
|
|
419
419
|
|
|
@@ -428,7 +428,7 @@ RSpec.describe Moxml::SAX do
|
|
|
428
428
|
|
|
429
429
|
it "documents comment limitation" do
|
|
430
430
|
# Ox SAX does not have separate comment events
|
|
431
|
-
skip "Ox SAX does not support separate comment events"
|
|
431
|
+
skip "D-10: Ox SAX does not support separate comment events"
|
|
432
432
|
|
|
433
433
|
xml_with_comment = "<root><!-- comment --><data>content</data></root>"
|
|
434
434
|
|
|
@@ -443,7 +443,7 @@ RSpec.describe Moxml::SAX do
|
|
|
443
443
|
|
|
444
444
|
it "documents processing instruction limitation" do
|
|
445
445
|
# Ox SAX does not have separate PI events
|
|
446
|
-
skip "Ox SAX does not support separate processing instruction events"
|
|
446
|
+
skip "D-10: Ox SAX does not support separate processing instruction events"
|
|
447
447
|
|
|
448
448
|
xml_with_pi = '<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>'
|
|
449
449
|
|
|
@@ -714,7 +714,7 @@ RSpec.describe Moxml::SAX do
|
|
|
714
714
|
end
|
|
715
715
|
|
|
716
716
|
it "handles errors gracefully" do
|
|
717
|
-
skip "Oga SAX parser may be more lenient with malformed XML"
|
|
717
|
+
skip "D-11: Oga SAX parser may be more lenient with malformed XML"
|
|
718
718
|
|
|
719
719
|
invalid_xml = "<book><title>Unclosed"
|
|
720
720
|
|
|
@@ -1024,7 +1024,7 @@ RSpec.describe Moxml::SAX do
|
|
|
1024
1024
|
|
|
1025
1025
|
it "documents CDATA limitation (inherited from Ox)" do
|
|
1026
1026
|
# HeadedOx inherits Ox's SAX implementation, which does not have separate CDATA events
|
|
1027
|
-
skip "HeadedOx SAX (inherited from Ox) does not support separate CDATA events"
|
|
1027
|
+
skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate CDATA events"
|
|
1028
1028
|
|
|
1029
1029
|
xml_with_cdata = "<root><![CDATA[special content]]></root>"
|
|
1030
1030
|
|
|
@@ -1038,7 +1038,7 @@ RSpec.describe Moxml::SAX do
|
|
|
1038
1038
|
|
|
1039
1039
|
it "documents comment limitation (inherited from Ox)" do
|
|
1040
1040
|
# HeadedOx inherits Ox's SAX implementation, which does not have separate comment events
|
|
1041
|
-
skip "HeadedOx SAX (inherited from Ox) does not support separate comment events"
|
|
1041
|
+
skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate comment events"
|
|
1042
1042
|
|
|
1043
1043
|
xml_with_comment = "<root><!-- comment --><data>content</data></root>"
|
|
1044
1044
|
|
|
@@ -1052,7 +1052,7 @@ RSpec.describe Moxml::SAX do
|
|
|
1052
1052
|
|
|
1053
1053
|
it "documents processing instruction limitation (inherited from Ox)" do
|
|
1054
1054
|
# HeadedOx inherits Ox's SAX implementation, which does not have separate PI events
|
|
1055
|
-
skip "HeadedOx SAX (inherited from Ox) does not support separate processing instruction events"
|
|
1055
|
+
skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate processing instruction events"
|
|
1056
1056
|
|
|
1057
1057
|
xml_with_pi = '<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>'
|
|
1058
1058
|
|
|
@@ -28,7 +28,7 @@ RSpec.describe Moxml::StructPlan do
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
STRUCT_PLAN_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
|
|
32
32
|
|
|
33
33
|
def with_adapter(name)
|
|
34
34
|
yield Moxml.new(name)
|
|
@@ -36,7 +36,7 @@ RSpec.describe Moxml::StructPlan do
|
|
|
36
36
|
skip "#{name} unavailable"
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
STRUCT_PLAN_ADAPTERS.each do |adapter_name|
|
|
40
40
|
describe "on #{adapter_name}" do
|
|
41
41
|
it "materializes the same structs as Moxml::Plan" do
|
|
42
42
|
with_adapter(adapter_name) do |ctx|
|