svg_conform 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +182 -21
- data/README.adoc +391 -989
- data/config/profiles/metanorma.yml +5 -0
- data/docs/api_reference.adoc +1355 -0
- data/docs/cli_guide.adoc +846 -0
- data/docs/reference_manifest.adoc +370 -0
- data/docs/requirements.adoc +68 -1
- data/examples/document_input_demo.rb +102 -0
- data/lib/svg_conform/document.rb +40 -1
- data/lib/svg_conform/profile.rb +15 -9
- data/lib/svg_conform/references/base_reference.rb +130 -0
- data/lib/svg_conform/references/id_definition.rb +38 -0
- data/lib/svg_conform/references/reference_classifier.rb +45 -0
- data/lib/svg_conform/references/reference_manifest.rb +129 -0
- data/lib/svg_conform/references.rb +11 -0
- data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +34 -43
- data/lib/svg_conform/requirements/id_collection_requirement.rb +38 -0
- data/lib/svg_conform/requirements/id_reference_requirement.rb +11 -0
- data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +3 -0
- data/lib/svg_conform/requirements/link_validation_requirement.rb +114 -31
- data/lib/svg_conform/requirements/no_external_css_requirement.rb +5 -2
- data/lib/svg_conform/requirements.rb +11 -9
- data/lib/svg_conform/sax_validation_handler.rb +16 -1
- data/lib/svg_conform/validation_context.rb +67 -1
- data/lib/svg_conform/validation_result.rb +43 -2
- data/lib/svg_conform/validator.rb +56 -16
- data/lib/svg_conform/version.rb +1 -1
- data/lib/svg_conform.rb +11 -2
- data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +1 -0
- data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +1 -0
- data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +1 -0
- data/spec/svg_conform/references/integration_spec.rb +206 -0
- data/spec/svg_conform/references/reference_classifier_spec.rb +142 -0
- data/spec/svg_conform/references/reference_manifest_spec.rb +307 -0
- data/spec/svg_conform/requirements/id_reference_state_spec.rb +93 -0
- data/spec/svg_conform/validator_input_types_spec.rb +172 -0
- metadata +17 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Reference Manifest Integration" do
|
|
6
|
+
let(:validator) { SvgConform::Validator.new(profile: "metanorma") }
|
|
7
|
+
|
|
8
|
+
describe "complete reference manifest generation" do
|
|
9
|
+
let(:svg_content) do
|
|
10
|
+
<<~SVG
|
|
11
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
12
|
+
<rect id="rect-1" x="0" y="0" width="100" height="100"/>
|
|
13
|
+
<circle id="circle-1" cx="50" cy="50" r="25"/>
|
|
14
|
+
<use href="#rect-1"/>
|
|
15
|
+
<use href="#missing-element"/>
|
|
16
|
+
<a href="urn:ietf:rfc:7996">RFC 7996</a>
|
|
17
|
+
<a href="https://www.example.com">Example</a>
|
|
18
|
+
<image href="data:image/png;base64,iVBORw0KGgo=" x="0" y="0" width="10" height="10"/>
|
|
19
|
+
<image href="./other.svg#fragment" x="0" y="0" width="10" height="10"/>
|
|
20
|
+
</svg>
|
|
21
|
+
SVG
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "builds complete manifest with IDs and references" do
|
|
25
|
+
result = validator.validate(svg_content)
|
|
26
|
+
manifest = result.reference_manifest
|
|
27
|
+
|
|
28
|
+
# Check IDs are collected
|
|
29
|
+
expect(manifest.available_ids.size).to eq(2)
|
|
30
|
+
expect(manifest.id_defined?("rect-1")).to be true
|
|
31
|
+
expect(manifest.id_defined?("circle-1")).to be true
|
|
32
|
+
|
|
33
|
+
# Check references are collected
|
|
34
|
+
expect(manifest.internal_references.size).to eq(3) # 2 uses + 1 data URI
|
|
35
|
+
expect(manifest.external_references.size).to eq(3) # 1 urn + 1 https + 1 relative
|
|
36
|
+
|
|
37
|
+
# Check statistics
|
|
38
|
+
stats = manifest.statistics
|
|
39
|
+
expect(stats[:total_ids]).to eq(2)
|
|
40
|
+
expect(stats[:total_references]).to eq(6)
|
|
41
|
+
expect(stats[:unresolved_internal]).to eq(1)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "identifies unresolved internal references" do
|
|
45
|
+
result = validator.validate(svg_content)
|
|
46
|
+
manifest = result.reference_manifest
|
|
47
|
+
|
|
48
|
+
unresolved = manifest.unresolved_internal_references
|
|
49
|
+
expect(unresolved.size).to eq(1)
|
|
50
|
+
expect(unresolved.first.value).to eq("#missing-element")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "groups references by type" do
|
|
54
|
+
result = validator.validate(svg_content)
|
|
55
|
+
manifest = result.reference_manifest
|
|
56
|
+
|
|
57
|
+
by_type = manifest.references_by_type
|
|
58
|
+
expect(by_type["InternalFragmentReference"].size).to eq(2) # 2 uses
|
|
59
|
+
expect(by_type["UrnReference"].size).to eq(1)
|
|
60
|
+
expect(by_type["ExternalUrlReference"].size).to eq(1)
|
|
61
|
+
expect(by_type["DataUriReference"].size).to eq(1)
|
|
62
|
+
expect(by_type["RelativePathReference"].size).to eq(1)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "validates internal references but not external ones" do
|
|
66
|
+
result = validator.validate(svg_content)
|
|
67
|
+
|
|
68
|
+
# Should have errors for unresolved internal reference
|
|
69
|
+
internal_ref_errors = result.errors.select do |e|
|
|
70
|
+
e.message.include?("missing-element")
|
|
71
|
+
end
|
|
72
|
+
expect(internal_ref_errors).not_to be_empty
|
|
73
|
+
|
|
74
|
+
# Should NOT have errors for external references
|
|
75
|
+
external_ref_errors = result.errors.select do |e|
|
|
76
|
+
e.message.include?("urn:") || e.message.include?("https://")
|
|
77
|
+
end
|
|
78
|
+
expect(external_ref_errors).to be_empty
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "ValidationResult convenience methods" do
|
|
83
|
+
let(:svg_content) do
|
|
84
|
+
<<~SVG
|
|
85
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
86
|
+
<rect id="target"/>
|
|
87
|
+
<use href="#target"/>
|
|
88
|
+
<a href="urn:test">Test</a>
|
|
89
|
+
</svg>
|
|
90
|
+
SVG
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "provides convenient accessors" do
|
|
94
|
+
result = validator.validate(svg_content)
|
|
95
|
+
|
|
96
|
+
expect(result.available_ids.size).to eq(1)
|
|
97
|
+
expect(result.internal_references.size).to eq(1)
|
|
98
|
+
expect(result.external_references.size).to eq(1)
|
|
99
|
+
expect(result.has_external_references?).to be true
|
|
100
|
+
expect(result.unresolved_internal_references).to be_empty
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "exports manifest in different formats" do
|
|
104
|
+
result = validator.validate(svg_content)
|
|
105
|
+
|
|
106
|
+
yaml_export = result.export_manifest(format: :yaml)
|
|
107
|
+
expect(yaml_export).to be_a(String)
|
|
108
|
+
expect(yaml_export).to include("target")
|
|
109
|
+
|
|
110
|
+
json_export = result.export_manifest(format: :json)
|
|
111
|
+
expect(json_export).to be_a(String)
|
|
112
|
+
parsed = JSON.parse(json_export)
|
|
113
|
+
expect(parsed["available_ids"]).to be_an(Array)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "cross-document reference scenario" do
|
|
118
|
+
let(:svg_with_external_fragment) do
|
|
119
|
+
<<~SVG
|
|
120
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
121
|
+
<rect id="local-element"/>
|
|
122
|
+
<use href="#local-element"/>
|
|
123
|
+
<use href="other-doc.svg#external-element"/>
|
|
124
|
+
</svg>
|
|
125
|
+
SVG
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "classifies external fragment references correctly" do
|
|
129
|
+
result = validator.validate(svg_with_external_fragment)
|
|
130
|
+
manifest = result.reference_manifest
|
|
131
|
+
|
|
132
|
+
# Local fragment should be internal
|
|
133
|
+
expect(manifest.internal_references.size).to eq(1)
|
|
134
|
+
expect(manifest.internal_references.first.value).to eq("#local-element")
|
|
135
|
+
|
|
136
|
+
# External fragment should be external (requires consumer validation)
|
|
137
|
+
expect(manifest.external_references.size).to eq(1)
|
|
138
|
+
external_ref = manifest.external_references.first
|
|
139
|
+
expect(external_ref).to be_a(SvgConform::References::RelativePathReference)
|
|
140
|
+
expect(external_ref.has_fragment?).to be true
|
|
141
|
+
expect(external_ref.fragment_component).to eq("external-element")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe "reference statistics and querying" do
|
|
146
|
+
let(:complex_svg) do
|
|
147
|
+
<<~SVG
|
|
148
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
149
|
+
<defs>
|
|
150
|
+
<linearGradient id="grad1">
|
|
151
|
+
<stop offset="0%" stop-color="red"/>
|
|
152
|
+
</linearGradient>
|
|
153
|
+
<clipPath id="clip1">
|
|
154
|
+
<rect x="0" y="0" width="50" height="50"/>
|
|
155
|
+
</clipPath>
|
|
156
|
+
</defs>
|
|
157
|
+
<rect id="rect1" fill="url(#grad1)" clip-path="url(#clip1)"/>
|
|
158
|
+
<use href="#rect1"/>
|
|
159
|
+
<use href="#rect1"/>
|
|
160
|
+
<a href="https://example.com">Link</a>
|
|
161
|
+
</svg>
|
|
162
|
+
SVG
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "tracks multiple references to same ID" do
|
|
166
|
+
result = validator.validate(complex_svg)
|
|
167
|
+
manifest = result.reference_manifest
|
|
168
|
+
|
|
169
|
+
rect1_refs = manifest.references_to_id("rect1")
|
|
170
|
+
expect(rect1_refs.size).to eq(2)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "provides accurate statistics" do
|
|
174
|
+
result = validator.validate(complex_svg)
|
|
175
|
+
stats = result.reference_statistics
|
|
176
|
+
|
|
177
|
+
expect(stats[:total_ids]).to eq(3) # grad1, clip1, rect1
|
|
178
|
+
expect(stats[:internal_references]).to eq(2) # 2 uses
|
|
179
|
+
expect(stats[:external_references]).to eq(1) # 1 https
|
|
180
|
+
expect(stats[:unresolved_internal]).to eq(0)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
describe "to_h includes reference manifest" do
|
|
185
|
+
let(:svg_content) do
|
|
186
|
+
<<~SVG
|
|
187
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 100 100">
|
|
188
|
+
<rect id="test"/>
|
|
189
|
+
<a href="urn:test">Test</a>
|
|
190
|
+
</svg>
|
|
191
|
+
SVG
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "includes manifest in hash representation" do
|
|
195
|
+
result = validator.validate(svg_content)
|
|
196
|
+
hash = result.to_h
|
|
197
|
+
|
|
198
|
+
expect(hash).to have_key(:reference_manifest)
|
|
199
|
+
manifest_data = hash[:reference_manifest]
|
|
200
|
+
expect(manifest_data).to have_key(:available_ids)
|
|
201
|
+
expect(manifest_data).to have_key(:internal_references)
|
|
202
|
+
expect(manifest_data).to have_key(:external_references)
|
|
203
|
+
expect(manifest_data).to have_key(:statistics)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe SvgConform::References::ReferenceClassifier do
|
|
6
|
+
describe ".classify" do
|
|
7
|
+
it "classifies internal fragment references" do
|
|
8
|
+
ref = described_class.classify(
|
|
9
|
+
"#element-id",
|
|
10
|
+
element_name: "use",
|
|
11
|
+
attribute_name: "href",
|
|
12
|
+
line_number: 10,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
expect(ref).to be_a(SvgConform::References::InternalFragmentReference)
|
|
16
|
+
expect(ref.value).to eq("#element-id")
|
|
17
|
+
expect(ref.internally_validatable?).to be true
|
|
18
|
+
expect(ref.requires_consumer_validation?).to be false
|
|
19
|
+
expect(ref.target_id).to eq("element-id")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "classifies URN references" do
|
|
23
|
+
ref = described_class.classify(
|
|
24
|
+
"urn:ietf:rfc:7996",
|
|
25
|
+
element_name: "a",
|
|
26
|
+
attribute_name: "href",
|
|
27
|
+
line_number: 42,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
expect(ref).to be_a(SvgConform::References::UrnReference)
|
|
31
|
+
expect(ref.value).to eq("urn:ietf:rfc:7996")
|
|
32
|
+
expect(ref.internally_validatable?).to be false
|
|
33
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
34
|
+
expect(ref.namespace).to eq("ietf")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "classifies external URL references (http)" do
|
|
38
|
+
ref = described_class.classify(
|
|
39
|
+
"http://www.example.com/resource",
|
|
40
|
+
element_name: "a",
|
|
41
|
+
attribute_name: "href",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
expect(ref).to be_a(SvgConform::References::ExternalUrlReference)
|
|
45
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
46
|
+
expect(ref.protocol).to eq("http")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "classifies external URL references (https)" do
|
|
50
|
+
ref = described_class.classify(
|
|
51
|
+
"https://www.example.com/resource",
|
|
52
|
+
element_name: "a",
|
|
53
|
+
attribute_name: "href",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(ref).to be_a(SvgConform::References::ExternalUrlReference)
|
|
57
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
58
|
+
expect(ref.protocol).to eq("https")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "classifies data URI references" do
|
|
62
|
+
ref = described_class.classify(
|
|
63
|
+
"data:image/png;base64,iVBORw0KGgo=",
|
|
64
|
+
element_name: "image",
|
|
65
|
+
attribute_name: "href",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
expect(ref).to be_a(SvgConform::References::DataUriReference)
|
|
69
|
+
expect(ref.internally_validatable?).to be true
|
|
70
|
+
expect(ref.media_type).to eq("image/png")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "classifies relative path references starting with ./" do
|
|
74
|
+
ref = described_class.classify(
|
|
75
|
+
"./other-doc.svg#element",
|
|
76
|
+
element_name: "use",
|
|
77
|
+
attribute_name: "href",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
expect(ref).to be_a(SvgConform::References::RelativePathReference)
|
|
81
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
82
|
+
expect(ref.has_fragment?).to be true
|
|
83
|
+
expect(ref.path_component).to eq("./other-doc.svg")
|
|
84
|
+
expect(ref.fragment_component).to eq("element")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "classifies relative path references starting with /" do
|
|
88
|
+
ref = described_class.classify(
|
|
89
|
+
"/absolute/path.svg",
|
|
90
|
+
element_name: "use",
|
|
91
|
+
attribute_name: "href",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expect(ref).to be_a(SvgConform::References::RelativePathReference)
|
|
95
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "classifies bare paths as relative references" do
|
|
99
|
+
ref = described_class.classify(
|
|
100
|
+
"other-doc.svg",
|
|
101
|
+
element_name: "use",
|
|
102
|
+
attribute_name: "href",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
expect(ref).to be_a(SvgConform::References::RelativePathReference)
|
|
106
|
+
expect(ref.requires_consumer_validation?).to be true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "returns nil for nil value" do
|
|
110
|
+
ref = described_class.classify(
|
|
111
|
+
nil,
|
|
112
|
+
element_name: "use",
|
|
113
|
+
attribute_name: "href",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
expect(ref).to be_nil
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "returns nil for empty value" do
|
|
120
|
+
ref = described_class.classify(
|
|
121
|
+
"",
|
|
122
|
+
element_name: "use",
|
|
123
|
+
attribute_name: "href",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
expect(ref).to be_nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "captures line and column information" do
|
|
130
|
+
ref = described_class.classify(
|
|
131
|
+
"#element-id",
|
|
132
|
+
element_name: "use",
|
|
133
|
+
attribute_name: "href",
|
|
134
|
+
line_number: 42,
|
|
135
|
+
column_number: 15,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
expect(ref.line_number).to eq(42)
|
|
139
|
+
expect(ref.column_number).to eq(15)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe SvgConform::References::ReferenceManifest do
|
|
6
|
+
let(:manifest) { described_class.new(source_document: "test.svg") }
|
|
7
|
+
|
|
8
|
+
describe "#initialize" do
|
|
9
|
+
it "creates an empty manifest" do
|
|
10
|
+
expect(manifest.source_document).to eq("test.svg")
|
|
11
|
+
expect(manifest.available_ids).to be_empty
|
|
12
|
+
expect(manifest.internal_references).to be_empty
|
|
13
|
+
expect(manifest.external_references).to be_empty
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#register_id" do
|
|
18
|
+
it "registers ID definitions" do
|
|
19
|
+
manifest.register_id("element-1", element_name: "rect", line_number: 10)
|
|
20
|
+
|
|
21
|
+
expect(manifest.available_ids.size).to eq(1)
|
|
22
|
+
expect(manifest.available_ids.first.id_value).to eq("element-1")
|
|
23
|
+
expect(manifest.available_ids.first.element_name).to eq("rect")
|
|
24
|
+
expect(manifest.available_ids.first.line_number).to eq(10)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "registers multiple IDs" do
|
|
28
|
+
manifest.register_id("id-1", element_name: "rect")
|
|
29
|
+
manifest.register_id("id-2", element_name: "circle")
|
|
30
|
+
|
|
31
|
+
expect(manifest.available_ids.size).to eq(2)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "#register_reference" do
|
|
36
|
+
it "separates internal and external references" do
|
|
37
|
+
internal_ref = SvgConform::References::InternalFragmentReference.new(
|
|
38
|
+
value: "#element-1",
|
|
39
|
+
element_name: "use",
|
|
40
|
+
attribute_name: "href",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
external_ref = SvgConform::References::UrnReference.new(
|
|
44
|
+
value: "urn:test",
|
|
45
|
+
element_name: "a",
|
|
46
|
+
attribute_name: "href",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
manifest.register_reference(internal_ref)
|
|
50
|
+
manifest.register_reference(external_ref)
|
|
51
|
+
|
|
52
|
+
expect(manifest.internal_references.size).to eq(1)
|
|
53
|
+
expect(manifest.external_references.size).to eq(1)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "#id_defined?" do
|
|
58
|
+
before do
|
|
59
|
+
manifest.register_id("element-1", element_name: "rect")
|
|
60
|
+
manifest.register_id("element-2", element_name: "circle")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "returns true for defined IDs" do
|
|
64
|
+
expect(manifest.id_defined?("element-1")).to be true
|
|
65
|
+
expect(manifest.id_defined?("element-2")).to be true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "returns false for undefined IDs" do
|
|
69
|
+
expect(manifest.id_defined?("missing-element")).to be false
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "#references_to_id" do
|
|
74
|
+
before do
|
|
75
|
+
manifest.register_id("target-element", element_name: "rect")
|
|
76
|
+
|
|
77
|
+
ref1 = SvgConform::References::InternalFragmentReference.new(
|
|
78
|
+
value: "#target-element",
|
|
79
|
+
element_name: "use",
|
|
80
|
+
attribute_name: "href",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
ref2 = SvgConform::References::InternalFragmentReference.new(
|
|
84
|
+
value: "#target-element",
|
|
85
|
+
element_name: "a",
|
|
86
|
+
attribute_name: "href",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
ref3 = SvgConform::References::InternalFragmentReference.new(
|
|
90
|
+
value: "#other-element",
|
|
91
|
+
element_name: "use",
|
|
92
|
+
attribute_name: "href",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
manifest.register_reference(ref1)
|
|
96
|
+
manifest.register_reference(ref2)
|
|
97
|
+
manifest.register_reference(ref3)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "returns all references to a specific ID" do
|
|
101
|
+
refs = manifest.references_to_id("target-element")
|
|
102
|
+
expect(refs.size).to eq(2)
|
|
103
|
+
expect(refs.all? { |r| r.target_id == "target-element" }).to be true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "returns empty array for ID with no references" do
|
|
107
|
+
refs = manifest.references_to_id("unreferenced-element")
|
|
108
|
+
expect(refs).to be_empty
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe "#references_by_type" do
|
|
113
|
+
before do
|
|
114
|
+
manifest.register_reference(
|
|
115
|
+
SvgConform::References::InternalFragmentReference.new(
|
|
116
|
+
value: "#id-1",
|
|
117
|
+
element_name: "use",
|
|
118
|
+
attribute_name: "href",
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
manifest.register_reference(
|
|
123
|
+
SvgConform::References::UrnReference.new(
|
|
124
|
+
value: "urn:test",
|
|
125
|
+
element_name: "a",
|
|
126
|
+
attribute_name: "href",
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
manifest.register_reference(
|
|
131
|
+
SvgConform::References::ExternalUrlReference.new(
|
|
132
|
+
value: "https://example.com",
|
|
133
|
+
element_name: "a",
|
|
134
|
+
attribute_name: "href",
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "groups references by type" do
|
|
140
|
+
by_type = manifest.references_by_type
|
|
141
|
+
|
|
142
|
+
expect(by_type.keys).to include(
|
|
143
|
+
"InternalFragmentReference",
|
|
144
|
+
"UrnReference",
|
|
145
|
+
"ExternalUrlReference",
|
|
146
|
+
)
|
|
147
|
+
expect(by_type["InternalFragmentReference"].size).to eq(1)
|
|
148
|
+
expect(by_type["UrnReference"].size).to eq(1)
|
|
149
|
+
expect(by_type["ExternalUrlReference"].size).to eq(1)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe "#unresolved_internal_references" do
|
|
154
|
+
before do
|
|
155
|
+
manifest.register_id("element-1", element_name: "rect")
|
|
156
|
+
|
|
157
|
+
ref1 = SvgConform::References::InternalFragmentReference.new(
|
|
158
|
+
value: "#element-1",
|
|
159
|
+
element_name: "use",
|
|
160
|
+
attribute_name: "href",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
ref2 = SvgConform::References::InternalFragmentReference.new(
|
|
164
|
+
value: "#missing-element",
|
|
165
|
+
element_name: "use",
|
|
166
|
+
attribute_name: "href",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
ref3 = SvgConform::References::InternalFragmentReference.new(
|
|
170
|
+
value: "#another-missing",
|
|
171
|
+
element_name: "use",
|
|
172
|
+
attribute_name: "href",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
manifest.register_reference(ref1)
|
|
176
|
+
manifest.register_reference(ref2)
|
|
177
|
+
manifest.register_reference(ref3)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "identifies references to non-existent IDs" do
|
|
181
|
+
unresolved = manifest.unresolved_internal_references
|
|
182
|
+
|
|
183
|
+
expect(unresolved.size).to eq(2)
|
|
184
|
+
expect(unresolved.map(&:value)).to include("#missing-element",
|
|
185
|
+
"#another-missing")
|
|
186
|
+
expect(unresolved.map(&:value)).not_to include("#element-1")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "returns empty array when all references are resolved" do
|
|
190
|
+
manifest2 = described_class.new
|
|
191
|
+
manifest2.register_id("element-1", element_name: "rect")
|
|
192
|
+
|
|
193
|
+
ref = SvgConform::References::InternalFragmentReference.new(
|
|
194
|
+
value: "#element-1",
|
|
195
|
+
element_name: "use",
|
|
196
|
+
attribute_name: "href",
|
|
197
|
+
)
|
|
198
|
+
manifest2.register_reference(ref)
|
|
199
|
+
|
|
200
|
+
expect(manifest2.unresolved_internal_references).to be_empty
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe "#statistics" do
|
|
205
|
+
before do
|
|
206
|
+
manifest.register_id("id-1", element_name: "rect")
|
|
207
|
+
manifest.register_id("id-2", element_name: "circle")
|
|
208
|
+
|
|
209
|
+
manifest.register_reference(
|
|
210
|
+
SvgConform::References::InternalFragmentReference.new(
|
|
211
|
+
value: "#id-1",
|
|
212
|
+
element_name: "use",
|
|
213
|
+
attribute_name: "href",
|
|
214
|
+
),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
manifest.register_reference(
|
|
218
|
+
SvgConform::References::InternalFragmentReference.new(
|
|
219
|
+
value: "#missing",
|
|
220
|
+
element_name: "use",
|
|
221
|
+
attribute_name: "href",
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
manifest.register_reference(
|
|
226
|
+
SvgConform::References::UrnReference.new(
|
|
227
|
+
value: "urn:test",
|
|
228
|
+
element_name: "a",
|
|
229
|
+
attribute_name: "href",
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
manifest.register_reference(
|
|
234
|
+
SvgConform::References::ExternalUrlReference.new(
|
|
235
|
+
value: "https://example.com",
|
|
236
|
+
element_name: "a",
|
|
237
|
+
attribute_name: "href",
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it "provides comprehensive statistics" do
|
|
243
|
+
stats = manifest.statistics
|
|
244
|
+
|
|
245
|
+
expect(stats[:total_ids]).to eq(2)
|
|
246
|
+
expect(stats[:total_references]).to eq(4)
|
|
247
|
+
expect(stats[:internal_references]).to eq(2)
|
|
248
|
+
expect(stats[:external_references]).to eq(2)
|
|
249
|
+
expect(stats[:unresolved_internal]).to eq(1)
|
|
250
|
+
expect(stats[:references_by_type]["InternalFragmentReference"]).to eq(2)
|
|
251
|
+
expect(stats[:references_by_type]["UrnReference"]).to eq(1)
|
|
252
|
+
expect(stats[:references_by_type]["ExternalUrlReference"]).to eq(1)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
describe "#to_h" do
|
|
257
|
+
before do
|
|
258
|
+
manifest.register_id("test-id", element_name: "rect", line_number: 5)
|
|
259
|
+
|
|
260
|
+
manifest.register_reference(
|
|
261
|
+
SvgConform::References::InternalFragmentReference.new(
|
|
262
|
+
value: "#test-id",
|
|
263
|
+
element_name: "use",
|
|
264
|
+
attribute_name: "href",
|
|
265
|
+
line_number: 10,
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "exports manifest as hash" do
|
|
271
|
+
hash = manifest.to_h
|
|
272
|
+
|
|
273
|
+
expect(hash[:source_document]).to eq("test.svg")
|
|
274
|
+
expect(hash[:available_ids]).to be_an(Array)
|
|
275
|
+
expect(hash[:internal_references]).to be_an(Array)
|
|
276
|
+
expect(hash[:external_references]).to be_an(Array)
|
|
277
|
+
expect(hash[:statistics]).to be_a(Hash)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
describe "export formats" do
|
|
282
|
+
before do
|
|
283
|
+
manifest.register_id("test-id", element_name: "rect")
|
|
284
|
+
manifest.register_reference(
|
|
285
|
+
SvgConform::References::UrnReference.new(
|
|
286
|
+
value: "urn:test",
|
|
287
|
+
element_name: "a",
|
|
288
|
+
attribute_name: "href",
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it "exports as YAML" do
|
|
294
|
+
yaml = manifest.to_yaml
|
|
295
|
+
expect(yaml).to be_a(String)
|
|
296
|
+
expect(yaml).to include("test.svg")
|
|
297
|
+
expect(yaml).to include("test-id")
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "exports as JSON" do
|
|
301
|
+
json = manifest.to_json
|
|
302
|
+
expect(json).to be_a(String)
|
|
303
|
+
parsed = JSON.parse(json)
|
|
304
|
+
expect(parsed["source_document"]).to eq("test.svg")
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|