crawlscope 0.7.2 → 0.8.1
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/CHANGELOG.md +27 -0
- data/README.md +95 -18
- data/lib/crawlscope/browser.rb +18 -1
- data/lib/crawlscope/configuration.rb +6 -1
- data/lib/crawlscope/crawl.rb +5 -3
- data/lib/crawlscope/http.rb +13 -2
- data/lib/crawlscope/page.rb +8 -0
- data/lib/crawlscope/reporter.rb +144 -61
- data/lib/crawlscope/request_headers.rb +34 -0
- data/lib/crawlscope/result.rb +4 -0
- data/lib/crawlscope/rules/content_quality.rb +5 -5
- data/lib/crawlscope/rules/indexability.rb +1 -1
- data/lib/crawlscope/rules/links.rb +28 -34
- data/lib/crawlscope/rules/metadata.rb +25 -25
- data/lib/crawlscope/rules/structured_data.rb +13 -4
- data/lib/crawlscope/rules/uniqueness.rb +9 -7
- data/lib/crawlscope/run.rb +0 -10
- data/lib/crawlscope/schema_registry.rb +17 -1
- data/lib/crawlscope/server_timing/reporter.rb +114 -0
- data/lib/crawlscope/server_timing/summary.rb +107 -0
- data/lib/crawlscope/server_timing.rb +138 -0
- data/lib/crawlscope/sitemap.rb +17 -5
- data/lib/crawlscope/structured_data/error_formatter.rb +42 -0
- data/lib/crawlscope/structured_data/reporter.rb +10 -10
- data/lib/crawlscope/version.rb +1 -1
- data/lib/generators/crawlscope/templates/initializer.rb.tt +1 -1
- data/test/crawlscope/browser_test.rb +56 -3
- data/test/crawlscope/configuration_test.rb +50 -10
- data/test/crawlscope/crawl_test.rb +65 -0
- data/test/crawlscope/http_test.rb +60 -1
- data/test/crawlscope/install_generator_test.rb +9 -11
- data/test/crawlscope/links_rule_test.rb +40 -4
- data/test/crawlscope/metadata_rule_test.rb +37 -4
- data/test/crawlscope/reporter_test.rb +249 -20
- data/test/crawlscope/run_test.rb +4 -12
- data/test/crawlscope/schema_registry_test.rb +33 -0
- data/test/crawlscope/server_timing_summary_test.rb +87 -0
- data/test/crawlscope/server_timing_test.rb +101 -0
- data/test/crawlscope/sitemap_test.rb +21 -0
- data/test/crawlscope/structured_data_error_formatter_test.rb +46 -0
- data/test/crawlscope/structured_data_reporter_test.rb +26 -0
- data/test/crawlscope/structured_data_rule_test.rb +2 -0
- data/test/crawlscope/uniqueness_rule_test.rb +46 -3
- metadata +10 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class CrawlscopeStructuredDataErrorFormatterTest < Minitest::Test
|
|
6
|
+
def test_formats_missing_root_and_nested_fields_from_real_schema_errors
|
|
7
|
+
registry = Crawlscope::SchemaRegistry.default
|
|
8
|
+
errors = registry.validate({"@type" => "WebApplication", "name" => "App", "review" => {"@type" => "Review"}})
|
|
9
|
+
|
|
10
|
+
assert_equal "WebApplication.review/reviewRating: is required", format_error(errors.first)
|
|
11
|
+
assert_equal "Article.headline: is required", format_error(registry.validate({"@type" => "Article"}).first)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_preserves_unknown_constraints_without_schema_noise
|
|
15
|
+
error = {"field" => "price", "type" => "Product", "issue" => "The property '#/price' must satisfy a custom constraint in schema 3bdea3a8-461f-5eae-9d73-3243b48b4bc8"}
|
|
16
|
+
|
|
17
|
+
assert_equal "Product.price: must satisfy a custom constraint", format_error(error)
|
|
18
|
+
assert_equal "$: Unknown error", format_error({})
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_formats_custom_minimum_length_constraint
|
|
22
|
+
registry = Crawlscope::SchemaRegistry.new(schemas: {"Thing" => {type: "object", properties: {name: {type: "string", minLength: 3}}}})
|
|
23
|
+
error = registry.validate({"@type" => "Thing", "name" => "x"}).first
|
|
24
|
+
|
|
25
|
+
assert_equal "Thing.name: 1 character; minimum 3", format_error(error)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_formats_measured_headline_length_with_symbol_or_json_keys
|
|
29
|
+
error = Crawlscope::SchemaRegistry.default.validate({"@type" => "Article", "headline" => "é" * 111}).first
|
|
30
|
+
|
|
31
|
+
assert_equal "Article.headline: 111 characters; maximum 110", format_error(error)
|
|
32
|
+
assert_equal "Article.headline: 111 characters; maximum 110", format_error(JSON.parse(JSON.generate(error)))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_formats_legacy_length_errors_without_a_measurement
|
|
36
|
+
error = {type: "Article", field: "headline", issue: "The property '#/headline' was not of a maximum string length of 110"}
|
|
37
|
+
|
|
38
|
+
assert_equal "Article.headline: exceeds 110 characters", format_error(error)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def format_error(error)
|
|
44
|
+
Crawlscope::StructuredData::ErrorFormatter.format(error)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -29,4 +29,30 @@ class CrawlscopeStructuredDataReporterTest < Minitest::Test
|
|
|
29
29
|
assert_includes output, "headline: is required"
|
|
30
30
|
assert_includes output, "/tmp/structured_data_report.json"
|
|
31
31
|
end
|
|
32
|
+
|
|
33
|
+
def test_formats_real_schema_errors_in_summary_and_detailed_reports
|
|
34
|
+
errors = Crawlscope::SchemaRegistry.default.validate({"@type" => "Article", "headline" => "x" * 111})
|
|
35
|
+
result = Crawlscope::StructuredData::Audit::Outcome.new(entries: [
|
|
36
|
+
Crawlscope::StructuredData::Audit::Page.new(
|
|
37
|
+
url: "https://example.com/article", status: 200,
|
|
38
|
+
structured_items: [{source: "json-ld", data: {"@type" => "Article"}}],
|
|
39
|
+
errors: [{type: "Article", source: "json-ld", errors: errors}],
|
|
40
|
+
fetch_error: nil, content_type: "text/html", skipped_reason: nil
|
|
41
|
+
)
|
|
42
|
+
])
|
|
43
|
+
io = StringIO.new
|
|
44
|
+
reporter = Crawlscope::StructuredData::Reporter.new(io: io)
|
|
45
|
+
|
|
46
|
+
reporter.report(result)
|
|
47
|
+
assert_includes io.string, "JSON-LD:\n - Article.headline: 111 characters; maximum 110"
|
|
48
|
+
refute_includes io.string, "in schema"
|
|
49
|
+
|
|
50
|
+
io.truncate(0)
|
|
51
|
+
io.rewind
|
|
52
|
+
reporter.details(result, debug: false, renderer: :http)
|
|
53
|
+
assert_includes io.string, "Validation: FAILED\n JSON-LD:\n - Article.headline: 111 characters; maximum 110"
|
|
54
|
+
refute_includes io.string, "=" * 80
|
|
55
|
+
refute_includes io.string, "in schema"
|
|
56
|
+
assert_includes errors.first[:issue], "in schema"
|
|
57
|
+
end
|
|
32
58
|
end
|
|
@@ -31,6 +31,8 @@ class CrawlscopeStructuredDataRuleTest < Minitest::Test
|
|
|
31
31
|
|
|
32
32
|
assert_equal [:structured_data_schema_error], issues.to_a.map(&:code)
|
|
33
33
|
assert_includes issues.to_a.first.message, "headline"
|
|
34
|
+
assert_equal "JSON-LD Article.headline: is required", issues.to_a.first.message
|
|
35
|
+
assert_includes issues.to_a.first.details[:errors].first[:issue], "in schema"
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def test_reports_parse_errors_for_invalid_json_ld
|
|
@@ -44,6 +44,49 @@ class CrawlscopeUniquenessRuleTest < Minitest::Test
|
|
|
44
44
|
assert_operator issue.details[:similarity], :>=, issue.details[:threshold]
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def test_does_not_report_redirect_aliases_as_duplicate_pages
|
|
48
|
+
pages = [
|
|
49
|
+
page(url: "https://example.com/old", final_url: "https://example.com/current"),
|
|
50
|
+
page(url: "https://example.com/alias", final_url: "https://example.com/current"),
|
|
51
|
+
page(url: "https://example.com/current")
|
|
52
|
+
]
|
|
53
|
+
issues = Crawlscope::IssueCollection.new
|
|
54
|
+
|
|
55
|
+
Crawlscope::Rules::Uniqueness.new.call(urls: pages.map(&:url), pages: pages, issues: issues, context: {})
|
|
56
|
+
|
|
57
|
+
assert_empty issues.to_a
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_reports_direct_duplicate_pages_without_including_redirect_aliases
|
|
61
|
+
pages = [
|
|
62
|
+
page(url: "https://example.com/old", final_url: "https://example.com/a"),
|
|
63
|
+
page(url: "https://example.com/a"),
|
|
64
|
+
page(url: "https://example.com/b")
|
|
65
|
+
]
|
|
66
|
+
issues = Crawlscope::IssueCollection.new
|
|
67
|
+
|
|
68
|
+
Crawlscope::Rules::Uniqueness.new.call(urls: pages.map(&:url), pages: pages, issues: issues, context: {})
|
|
69
|
+
|
|
70
|
+
assert_equal 4, issues.size
|
|
71
|
+
issues.each do |issue|
|
|
72
|
+
assert_equal ["https://example.com/a", "https://example.com/b"], issue.details[:urls]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_excludes_redirects_from_near_duplicate_checks_and_scan_limit
|
|
77
|
+
pages = [
|
|
78
|
+
page(url: "https://example.com/old", final_url: "https://example.com/other", content: near_duplicate_content("reliable")),
|
|
79
|
+
page(url: "https://example.com/current", content: near_duplicate_content("dependable"))
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[Crawlscope::Rules::Uniqueness.new, Crawlscope::Rules::Uniqueness.new(max_near_duplicate_pages: 1)].each do |rule|
|
|
83
|
+
issues = Crawlscope::IssueCollection.new
|
|
84
|
+
rule.call(urls: pages.map(&:url), pages: pages, issues: issues, context: {})
|
|
85
|
+
|
|
86
|
+
assert_empty issues.to_a
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
47
90
|
def test_skips_near_duplicate_scan_when_page_count_exceeds_limit
|
|
48
91
|
issues = Crawlscope::IssueCollection.new
|
|
49
92
|
rule = Crawlscope::Rules::Uniqueness.new(max_near_duplicate_pages: 1)
|
|
@@ -72,7 +115,7 @@ class CrawlscopeUniquenessRuleTest < Minitest::Test
|
|
|
72
115
|
TEXT
|
|
73
116
|
end
|
|
74
117
|
|
|
75
|
-
def page(url:, content: nil, canonical: nil)
|
|
118
|
+
def page(url:, content: nil, canonical: nil, final_url: url)
|
|
76
119
|
repeated_text = content || ("Useful content " * 30).strip
|
|
77
120
|
canonical_tag = canonical ? %(<link rel="canonical" href="#{canonical}">) : ""
|
|
78
121
|
body = <<~HTML
|
|
@@ -91,8 +134,8 @@ class CrawlscopeUniquenessRuleTest < Minitest::Test
|
|
|
91
134
|
Crawlscope::Page.new(
|
|
92
135
|
url: url,
|
|
93
136
|
normalized_url: url,
|
|
94
|
-
final_url:
|
|
95
|
-
normalized_final_url:
|
|
137
|
+
final_url: final_url,
|
|
138
|
+
normalized_final_url: final_url,
|
|
96
139
|
status: 200,
|
|
97
140
|
headers: {"content-type" => "text/html"},
|
|
98
141
|
body: body,
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: crawlscope
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paulo Fidalgo
|
|
@@ -238,6 +238,7 @@ files:
|
|
|
238
238
|
- lib/crawlscope/railtie.rb
|
|
239
239
|
- lib/crawlscope/rake_tasks.rb
|
|
240
240
|
- lib/crawlscope/reporter.rb
|
|
241
|
+
- lib/crawlscope/request_headers.rb
|
|
241
242
|
- lib/crawlscope/result.rb
|
|
242
243
|
- lib/crawlscope/rule_registry.rb
|
|
243
244
|
- lib/crawlscope/rules/content_quality.rb
|
|
@@ -249,10 +250,14 @@ files:
|
|
|
249
250
|
- lib/crawlscope/run.rb
|
|
250
251
|
- lib/crawlscope/schema_registry.rb
|
|
251
252
|
- lib/crawlscope/schemas.rb
|
|
253
|
+
- lib/crawlscope/server_timing.rb
|
|
254
|
+
- lib/crawlscope/server_timing/reporter.rb
|
|
255
|
+
- lib/crawlscope/server_timing/summary.rb
|
|
252
256
|
- lib/crawlscope/sitemap.rb
|
|
253
257
|
- lib/crawlscope/structured_data/audit.rb
|
|
254
258
|
- lib/crawlscope/structured_data/check.rb
|
|
255
259
|
- lib/crawlscope/structured_data/document.rb
|
|
260
|
+
- lib/crawlscope/structured_data/error_formatter.rb
|
|
256
261
|
- lib/crawlscope/structured_data/report.rb
|
|
257
262
|
- lib/crawlscope/structured_data/reporter.rb
|
|
258
263
|
- lib/crawlscope/structured_data/writer.rb
|
|
@@ -281,9 +286,12 @@ files:
|
|
|
281
286
|
- test/crawlscope/rule_registry_test.rb
|
|
282
287
|
- test/crawlscope/run_test.rb
|
|
283
288
|
- test/crawlscope/schema_registry_test.rb
|
|
289
|
+
- test/crawlscope/server_timing_summary_test.rb
|
|
290
|
+
- test/crawlscope/server_timing_test.rb
|
|
284
291
|
- test/crawlscope/sitemap_test.rb
|
|
285
292
|
- test/crawlscope/structured_data_audit_test.rb
|
|
286
293
|
- test/crawlscope/structured_data_document_test.rb
|
|
294
|
+
- test/crawlscope/structured_data_error_formatter_test.rb
|
|
287
295
|
- test/crawlscope/structured_data_report_test.rb
|
|
288
296
|
- test/crawlscope/structured_data_reporter_test.rb
|
|
289
297
|
- test/crawlscope/structured_data_rule_test.rb
|
|
@@ -322,7 +330,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
322
330
|
- !ruby/object:Gem::Version
|
|
323
331
|
version: '0'
|
|
324
332
|
requirements: []
|
|
325
|
-
rubygems_version: 4.0.
|
|
333
|
+
rubygems_version: 4.0.20
|
|
326
334
|
specification_version: 4
|
|
327
335
|
summary: Audit sitemap URLs for metadata, structured data, uniqueness, and links
|
|
328
336
|
test_files: []
|