govspeak 6.5.0 → 6.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -2
- data/README.md +6 -5
- data/Rakefile +7 -4
- data/lib/govspeak.rb +116 -116
- data/lib/govspeak/header_extractor.rb +1 -1
- data/lib/govspeak/html_sanitizer.rb +12 -9
- data/lib/govspeak/kramdown_overrides.rb +2 -2
- data/lib/govspeak/link_extractor.rb +6 -6
- data/lib/govspeak/post_processor.rb +34 -14
- data/lib/govspeak/presenters/attachment_presenter.rb +39 -39
- data/lib/govspeak/presenters/contact_presenter.rb +2 -2
- data/lib/govspeak/presenters/h_card_presenter.rb +3 -3
- data/lib/govspeak/presenters/image_presenter.rb +4 -4
- data/lib/govspeak/structured_header_extractor.rb +2 -2
- data/lib/govspeak/version.rb +1 -1
- data/lib/kramdown/parser/govuk.rb +6 -7
- data/test/blockquote_extra_quote_remover_test.rb +24 -26
- data/test/govspeak_attachment_link_test.rb +0 -2
- data/test/govspeak_attachment_test.rb +0 -2
- data/test/govspeak_attachments_image_test.rb +12 -14
- data/test/govspeak_attachments_inline_test.rb +22 -24
- data/test/govspeak_button_test.rb +29 -31
- data/test/govspeak_contacts_test.rb +29 -31
- data/test/govspeak_extract_contact_content_ids_test.rb +1 -3
- data/test/govspeak_images_bang_test.rb +37 -39
- data/test/govspeak_images_test.rb +43 -45
- data/test/govspeak_link_extractor_test.rb +1 -1
- data/test/govspeak_link_test.rb +1 -3
- data/test/govspeak_structured_headers_test.rb +7 -6
- data/test/govspeak_table_with_headers_test.rb +68 -21
- data/test/govspeak_test.rb +98 -101
- data/test/govspeak_test_helper.rb +1 -1
- data/test/html_sanitizer_test.rb +17 -9
- data/test/html_validator_test.rb +2 -2
- data/test/presenters/h_card_presenter_test.rb +39 -41
- data/test/test_helper.rb +12 -8
- metadata +52 -40
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "addressable/uri"
|
2
2
|
|
3
3
|
class Govspeak::HtmlSanitizer
|
4
4
|
class ImageSourceWhitelister
|
@@ -10,7 +10,7 @@ class Govspeak::HtmlSanitizer
|
|
10
10
|
return unless sanitize_context[:node_name] == "img"
|
11
11
|
|
12
12
|
node = sanitize_context[:node]
|
13
|
-
image_uri = Addressable::URI.parse(node[
|
13
|
+
image_uri = Addressable::URI.parse(node["src"])
|
14
14
|
unless image_uri.relative? || @allowed_image_hosts.include?(image_uri.host)
|
15
15
|
node.unlink # the node isn't sanitary. Remove it from the document.
|
16
16
|
end
|
@@ -25,8 +25,8 @@ class Govspeak::HtmlSanitizer
|
|
25
25
|
|
26
26
|
# Kramdown uses text-align to allow table cells to be aligned
|
27
27
|
# http://kramdown.gettalong.org/quickref.html#tables
|
28
|
-
if invalid_style_attribute?(node[
|
29
|
-
node.remove_attribute(
|
28
|
+
if invalid_style_attribute?(node["style"])
|
29
|
+
node.remove_attribute("style")
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -51,14 +51,17 @@ class Govspeak::HtmlSanitizer
|
|
51
51
|
def sanitize_config
|
52
52
|
Sanitize::Config.merge(
|
53
53
|
Sanitize::Config::RELAXED,
|
54
|
-
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link],
|
54
|
+
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path],
|
55
55
|
attributes: {
|
56
56
|
:all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
|
57
|
-
"a"
|
58
|
-
"
|
59
|
-
"
|
57
|
+
"a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data],
|
58
|
+
"svg" => Sanitize::Config::RELAXED[:attributes][:all] + %w[xmlns width height viewbox focusable],
|
59
|
+
"path" => Sanitize::Config::RELAXED[:attributes][:all] + %w[fill d],
|
60
|
+
"div" => [:data],
|
61
|
+
"th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
|
62
|
+
"td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
|
60
63
|
"govspeak-embed-attachment" => %w[content-id],
|
61
|
-
}
|
64
|
+
},
|
62
65
|
)
|
63
66
|
end
|
64
67
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Govspeak
|
2
2
|
module KramdownOverrides
|
3
|
-
#
|
3
|
+
# This depends on two internal parts of Kramdown.
|
4
4
|
# 1. Parser registry (kramdown/parser/kramdown.rb#define_parser)
|
5
5
|
# 2. Kramdown list regexes (kramdown/parser/kramdown/list.rb)
|
6
|
-
#
|
6
|
+
# Updating the Kramdown gem therefore also means updating this file to to
|
7
7
|
# match Kramdown's internals.
|
8
8
|
|
9
9
|
def self.with_kramdown_ordered_lists_disabled
|
@@ -14,14 +14,14 @@ module Govspeak
|
|
14
14
|
attr_reader :document, :website_root
|
15
15
|
|
16
16
|
def extract_links
|
17
|
-
document_anchors
|
18
|
-
map { |link| extract_href_from_link(link) }
|
19
|
-
reject(&:blank?)
|
17
|
+
document_anchors
|
18
|
+
.map { |link| extract_href_from_link(link) }
|
19
|
+
.reject(&:blank?)
|
20
20
|
end
|
21
21
|
|
22
22
|
def extract_href_from_link(link)
|
23
|
-
href = link[
|
24
|
-
if website_root && href.start_with?(
|
23
|
+
href = link["href"] || ""
|
24
|
+
if website_root && href.start_with?("/")
|
25
25
|
"#{website_root}#{href}"
|
26
26
|
else
|
27
27
|
href
|
@@ -29,7 +29,7 @@ module Govspeak
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def document_anchors
|
32
|
-
processed_govspeak.css(
|
32
|
+
processed_govspeak.css("a[href]").css('a:not([href^="mailto"])').css('a:not([href^="#"])')
|
33
33
|
end
|
34
34
|
|
35
35
|
def processed_govspeak
|
@@ -2,8 +2,8 @@ module Govspeak
|
|
2
2
|
class PostProcessor
|
3
3
|
@extensions = []
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
class << self
|
6
|
+
attr_reader :extensions
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.process(html, govspeak_document)
|
@@ -38,11 +38,11 @@ module Govspeak
|
|
38
38
|
el.children = xml
|
39
39
|
.gsub(
|
40
40
|
%r{<(div class="img")>(.*?)<(/div)>},
|
41
|
-
"<\\1>\\2<\\3>"
|
41
|
+
"<\\1>\\2<\\3>",
|
42
42
|
)
|
43
43
|
.gsub(
|
44
44
|
%r{<(figcaption)>(.*?)<(/figcaption&)gt;},
|
45
|
-
"<\\1>\\2<\\3>"
|
45
|
+
"<\\1>\\2<\\3>",
|
46
46
|
)
|
47
47
|
end
|
48
48
|
end
|
@@ -59,7 +59,7 @@ module Govspeak
|
|
59
59
|
attachment_html = GovukPublishingComponents.render(
|
60
60
|
"govuk_publishing_components/components/attachment",
|
61
61
|
attachment: attachment,
|
62
|
-
locale: govspeak_document.locale
|
62
|
+
locale: govspeak_document.locale,
|
63
63
|
)
|
64
64
|
el.swap(attachment_html)
|
65
65
|
end
|
@@ -77,7 +77,7 @@ module Govspeak
|
|
77
77
|
attachment_html = GovukPublishingComponents.render(
|
78
78
|
"govuk_publishing_components/components/attachment_link",
|
79
79
|
attachment: attachment,
|
80
|
-
locale: govspeak_document.locale
|
80
|
+
locale: govspeak_document.locale,
|
81
81
|
)
|
82
82
|
el.swap(attachment_html)
|
83
83
|
end
|
@@ -85,18 +85,38 @@ module Govspeak
|
|
85
85
|
|
86
86
|
extension("Add table headers and row / column scopes") do |document|
|
87
87
|
document.css("thead th").map do |el|
|
88
|
-
el.content = el.content.gsub(/^# /,
|
89
|
-
el.content = el.content.gsub(/[[:space:]]/,
|
90
|
-
el.name =
|
88
|
+
el.content = el.content.gsub(/^# /, "")
|
89
|
+
el.content = el.content.gsub(/[[:space:]]/, "") if el.content.blank? # Removes a strange whitespace in the cell if the cell is already blank.
|
90
|
+
el.name = "td" if el.content.blank? # This prevents a `th` with nothing inside it; a `td` is preferable.
|
91
91
|
el[:scope] = "col" if el.content.present? # `scope` shouldn't be used if there's nothing in the table heading.
|
92
92
|
end
|
93
93
|
|
94
94
|
document.css(":not(thead) tr td:first-child").map do |el|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
95
|
+
next unless el.content.match?(/^#($|\s.*$)/)
|
96
|
+
|
97
|
+
# Replace '# ' and '#', but not '#Word'.
|
98
|
+
# This runs on the first child of the element to preserve any links
|
99
|
+
el.children.first.content = el.children.first.content.gsub(/^#($|\s)/, "")
|
100
|
+
el.name = "th" if el.content.present? # This also prevents a `th` with nothing inside it; a `td` is preferable.
|
101
|
+
el[:scope] = "row" if el.content.present? # `scope` shouldn't be used if there's nothing in the table heading.
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
extension("use gem component for buttons") do |document|
|
106
|
+
document.css(".govuk-button").map do |el|
|
107
|
+
button_html = GovukPublishingComponents.render(
|
108
|
+
"govuk_publishing_components/components/button",
|
109
|
+
text: el.content,
|
110
|
+
href: el["href"],
|
111
|
+
start: el["data-start"],
|
112
|
+
data_attributes: {
|
113
|
+
module: el["data-module"],
|
114
|
+
"tracking-code": el["data-tracking-code"],
|
115
|
+
"tracking-name": el["data-tracking-name"],
|
116
|
+
},
|
117
|
+
).squish.gsub("> <", "><").gsub!(/\s+/, " ")
|
118
|
+
|
119
|
+
el.swap(button_html)
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
@@ -34,15 +34,15 @@ module Govspeak
|
|
34
34
|
def attachment_attributes
|
35
35
|
attributes = []
|
36
36
|
if file_extension == "html"
|
37
|
-
attributes << content_tag(:span,
|
37
|
+
attributes << content_tag(:span, "HTML", class: "type")
|
38
38
|
elsif attachment[:external?]
|
39
|
-
attributes << content_tag(:span, url, class:
|
39
|
+
attributes << content_tag(:span, url, class: "url")
|
40
40
|
else
|
41
|
-
attributes << content_tag(:span, humanized_content_type(file_extension), class:
|
42
|
-
attributes << content_tag(:span, number_to_human_size(attachment[:file_size]), class:
|
43
|
-
attributes << content_tag(:span, pluralize(attachment[:number_of_pages], "page"), class:
|
41
|
+
attributes << content_tag(:span, humanized_content_type(file_extension), class: "type") if file_extension
|
42
|
+
attributes << content_tag(:span, number_to_human_size(attachment[:file_size]), class: "file-size") if attachment[:file_size]
|
43
|
+
attributes << content_tag(:span, pluralize(attachment[:number_of_pages], "page"), class: "page-length") if attachment[:number_of_pages]
|
44
44
|
end
|
45
|
-
attributes.join(
|
45
|
+
attributes.join(", ").html_safe
|
46
46
|
end
|
47
47
|
|
48
48
|
MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE = "MS Word Document".freeze
|
@@ -55,46 +55,46 @@ module Govspeak
|
|
55
55
|
|
56
56
|
def humanized_content_type(file_extension)
|
57
57
|
file_extension_vs_humanized_content_type = {
|
58
|
-
"chm"
|
59
|
-
"csv"
|
60
|
-
"diff" => file_abbr_tag(
|
61
|
-
"doc"
|
58
|
+
"chm" => file_abbr_tag("CHM", "Microsoft Compiled HTML Help"),
|
59
|
+
"csv" => file_abbr_tag("CSV", "Comma-separated Values"),
|
60
|
+
"diff" => file_abbr_tag("DIFF", "Plain text differences"),
|
61
|
+
"doc" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
62
62
|
"docx" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
63
|
-
"dot"
|
64
|
-
"dxf"
|
65
|
-
"eps"
|
66
|
-
"gif"
|
67
|
-
"gml"
|
68
|
-
"html" => file_abbr_tag(
|
69
|
-
"ics" => file_abbr_tag(
|
70
|
-
"jpg"
|
71
|
-
"odp"
|
72
|
-
"ods"
|
73
|
-
"odt"
|
74
|
-
"pdf"
|
75
|
-
"png"
|
76
|
-
"ppt"
|
63
|
+
"dot" => file_abbr_tag("DOT", "MS Word Document Template"),
|
64
|
+
"dxf" => file_abbr_tag("DXF", "AutoCAD Drawing Exchange Format"),
|
65
|
+
"eps" => file_abbr_tag("EPS", "Encapsulated PostScript"),
|
66
|
+
"gif" => file_abbr_tag("GIF", "Graphics Interchange Format"),
|
67
|
+
"gml" => file_abbr_tag("GML", "Geography Markup Language"),
|
68
|
+
"html" => file_abbr_tag("HTML", "Hypertext Markup Language"),
|
69
|
+
"ics" => file_abbr_tag("ICS", "iCalendar file"),
|
70
|
+
"jpg" => "JPEG",
|
71
|
+
"odp" => file_abbr_tag("ODP", "OpenDocument Presentation"),
|
72
|
+
"ods" => file_abbr_tag("ODS", "OpenDocument Spreadsheet"),
|
73
|
+
"odt" => file_abbr_tag("ODT", "OpenDocument Text document"),
|
74
|
+
"pdf" => file_abbr_tag("PDF", "Portable Document Format"),
|
75
|
+
"png" => file_abbr_tag("PNG", "Portable Network Graphic"),
|
76
|
+
"ppt" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
77
77
|
"pptx" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
78
|
-
"ps"
|
79
|
-
"rdf"
|
80
|
-
"rtf"
|
81
|
-
"sch"
|
82
|
-
"txt"
|
83
|
-
"wsdl" => file_abbr_tag(
|
84
|
-
"xls"
|
85
|
-
"xlsm" => file_abbr_tag(
|
78
|
+
"ps" => file_abbr_tag("PS", "PostScript"),
|
79
|
+
"rdf" => file_abbr_tag("RDF", "Resource Description Framework"),
|
80
|
+
"rtf" => file_abbr_tag("RTF", "Rich Text Format"),
|
81
|
+
"sch" => file_abbr_tag("SCH", "XML based Schematic"),
|
82
|
+
"txt" => "Plain text",
|
83
|
+
"wsdl" => file_abbr_tag("WSDL", "Web Services Description Language"),
|
84
|
+
"xls" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
85
|
+
"xlsm" => file_abbr_tag("XLSM", "MS Excel Macro-Enabled Workbook"),
|
86
86
|
"xlsx" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
87
|
-
"xlt"
|
88
|
-
"xsd"
|
89
|
-
"xslt" => file_abbr_tag(
|
90
|
-
"zip"
|
87
|
+
"xlt" => file_abbr_tag("XLT", "MS Excel Spreadsheet Template"),
|
88
|
+
"xsd" => file_abbr_tag("XSD", "XML Schema"),
|
89
|
+
"xslt" => file_abbr_tag("XSLT", "Extensible Stylesheet Language Transformation"),
|
90
|
+
"zip" => file_abbr_tag("ZIP", "Zip archive"),
|
91
91
|
}
|
92
|
-
file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase,
|
92
|
+
file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, "")
|
93
93
|
end
|
94
94
|
|
95
95
|
def link(body, url, options = {})
|
96
|
-
options_str = options.map { |k, v| %
|
97
|
-
%
|
96
|
+
options_str = options.map { |k, v| %(#{encode(k)}="#{encode(v)}") }.join(" ")
|
97
|
+
%(<a href="#{encode(url)}" #{options_str}>#{body}</a>)
|
98
98
|
end
|
99
99
|
|
100
100
|
private
|
@@ -2,7 +2,7 @@ module Govspeak
|
|
2
2
|
class HCardPresenter
|
3
3
|
def self.address_formats
|
4
4
|
@address_formats ||= YAML.load_file(
|
5
|
-
File.expand_path(
|
5
|
+
File.expand_path("config/address_formats.yml", Govspeak.root),
|
6
6
|
)
|
7
7
|
end
|
8
8
|
|
@@ -44,7 +44,7 @@ module Govspeak
|
|
44
44
|
address.gsub!(/\{\{#{hcard_name}\}\}/, interpolate_address_property(our_name, hcard_name))
|
45
45
|
end
|
46
46
|
|
47
|
-
address.gsub(/^\n/,
|
47
|
+
address.gsub(/^\n/, "") # get rid of blank lines
|
48
48
|
.strip # get rid of any trailing whitespace
|
49
49
|
.gsub(/\n/, "<br />\n") # add break tags where appropriate
|
50
50
|
end
|
@@ -55,7 +55,7 @@ module Govspeak
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def default_format_string
|
58
|
-
self.class.address_formats[
|
58
|
+
self.class.address_formats["gb"]
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -32,10 +32,10 @@ module Govspeak
|
|
32
32
|
|
33
33
|
def figcaption_html
|
34
34
|
lines = []
|
35
|
-
lines <<
|
36
|
-
lines << %
|
37
|
-
lines << %
|
38
|
-
lines <<
|
35
|
+
lines << "<figcaption>"
|
36
|
+
lines << %(<p>#{caption}</p>) if caption.present?
|
37
|
+
lines << %(<p>#{I18n.t('govspeak.image.figure.credit', credit: credit)}</p>) if credit.present?
|
38
|
+
lines << "</figcaption>"
|
39
39
|
lines.join
|
40
40
|
end
|
41
41
|
end
|
@@ -48,9 +48,9 @@ module Govspeak
|
|
48
48
|
private :doc, :stack, :structured_headers
|
49
49
|
|
50
50
|
def headers_list
|
51
|
-
@headers_list ||= doc.headers.map
|
51
|
+
@headers_list ||= doc.headers.map do |h|
|
52
52
|
StructuredHeader.new(h.text, h.level, h.id, [])
|
53
|
-
|
53
|
+
end
|
54
54
|
end
|
55
55
|
|
56
56
|
def add_top_level(header)
|
data/lib/govspeak/version.rb
CHANGED
@@ -9,7 +9,7 @@ module Kramdown
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
define(:document_domains, Object, %w
|
12
|
+
define(:document_domains, Object, %w[www.gov.uk], <<~DESCRIPTION) do |val|
|
13
13
|
Defines the domains which are considered local to the document
|
14
14
|
|
15
15
|
Default: www.gov.uk
|
@@ -21,10 +21,10 @@ module Kramdown
|
|
21
21
|
|
22
22
|
module Parser
|
23
23
|
class Govuk < Kramdown::Parser::Kramdown
|
24
|
-
CUSTOM_INLINE_ELEMENTS = %w
|
24
|
+
CUSTOM_INLINE_ELEMENTS = %w[govspeak-embed-attachment-link].freeze
|
25
25
|
|
26
26
|
def initialize(source, options)
|
27
|
-
@document_domains = options[:document_domains] || %w
|
27
|
+
@document_domains = options[:document_domains] || %w[www.gov.uk]
|
28
28
|
super
|
29
29
|
end
|
30
30
|
|
@@ -33,13 +33,12 @@ module Kramdown
|
|
33
33
|
begin
|
34
34
|
host = Addressable::URI.parse(href).host
|
35
35
|
unless host.nil? || @document_domains.compact.include?(host)
|
36
|
-
element.attr[
|
36
|
+
element.attr["rel"] = "external"
|
37
37
|
end
|
38
|
-
# rubocop:disable Lint/
|
39
|
-
rescue Addressable::URI::InvalidURIError
|
38
|
+
rescue Addressable::URI::InvalidURIError # rubocop:disable Lint/SuppressedException
|
40
39
|
# it's safe to ignore these very *specific* exceptions
|
41
40
|
end
|
42
|
-
|
41
|
+
|
43
42
|
end
|
44
43
|
super
|
45
44
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require "test_helper"
|
4
2
|
|
5
3
|
class BlockquoteExtraQuoteRemoverTest < Minitest::Test
|
@@ -18,81 +16,81 @@ class BlockquoteExtraQuoteRemoverTest < Minitest::Test
|
|
18
16
|
end
|
19
17
|
|
20
18
|
test "ignores text without double quotes" do
|
21
|
-
assert_leaves_untouched %
|
19
|
+
assert_leaves_untouched %(no quotes\na few lines,\n\n\n\nbut no quotes\n\n)
|
22
20
|
end
|
23
21
|
|
24
22
|
test "ignores text without a quote symbol" do
|
25
|
-
assert_leaves_untouched %
|
23
|
+
assert_leaves_untouched %(quotes\n"but not with a > symbol")
|
26
24
|
end
|
27
25
|
|
28
26
|
test "transforms text with surrounding quotes" do
|
29
27
|
assert_remover_transforms(
|
30
|
-
%
|
31
|
-
%
|
28
|
+
%(He said:\n> "yes, it's true!"\n\napparently.) =>
|
29
|
+
%(He said:\n> yes, it's true!\n\napparently.),
|
32
30
|
)
|
33
31
|
end
|
34
32
|
|
35
33
|
test "leaves quotes in the middle of the string" do
|
36
34
|
assert_remover_transforms(
|
37
|
-
%
|
38
|
-
%
|
35
|
+
%(He said:\n> "yes, it's true!" whilst sipping a cocktail. "And yes, I did rather enjoy it." \n\napparently.) =>
|
36
|
+
%(He said:\n> yes, it's true!" whilst sipping a cocktail. "And yes, I did rather enjoy it.\n\napparently.),
|
39
37
|
)
|
40
38
|
end
|
41
39
|
|
42
40
|
test "leaves trailing text and quote intact" do
|
43
41
|
assert_remover_transforms(
|
44
|
-
%
|
45
|
-
%
|
42
|
+
%(He said:\n> "yes, it's true!" whilst sipping a cocktail.) =>
|
43
|
+
%(He said:\n> yes, it's true!" whilst sipping a cocktail.),
|
46
44
|
)
|
47
45
|
end
|
48
46
|
|
49
47
|
test "windows line breaks" do
|
50
48
|
assert_remover_transforms(
|
51
|
-
%
|
52
|
-
%
|
49
|
+
%(Sir George Young MP, said\r\n> "I welcome the positive public response to the e-petitions site, which is important way of building a bridge between people and Parliament.”\r\n## The special relationship) =>
|
50
|
+
%(Sir George Young MP, said\r\n> I welcome the positive public response to the e-petitions site, which is important way of building a bridge between people and Parliament.\r\n## The special relationship),
|
53
51
|
)
|
54
52
|
end
|
55
53
|
|
56
54
|
test "no space in front" do
|
57
55
|
assert_remover_transforms(
|
58
|
-
%
|
59
|
-
%
|
56
|
+
%(>"As we continue with the redundancy process we will ensure we retain the capabilities that our armed forces will require to meet the challenges of the future. The redundancy programme will not impact adversely on the current operations in Afghanistan, where our armed forces continue to fight so bravely on this country's behalf.") =>
|
57
|
+
%(> As we continue with the redundancy process we will ensure we retain the capabilities that our armed forces will require to meet the challenges of the future. The redundancy programme will not impact adversely on the current operations in Afghanistan, where our armed forces continue to fight so bravely on this country's behalf.),
|
60
58
|
)
|
61
59
|
end
|
62
60
|
|
63
61
|
test "handles space after a quote" do
|
64
62
|
assert_remover_transforms(
|
65
|
-
%
|
66
|
-
%
|
63
|
+
%(>" Test") =>
|
64
|
+
%(> Test),
|
67
65
|
)
|
68
66
|
end
|
69
67
|
|
70
68
|
test "remove double double quotes" do
|
71
69
|
assert_remover_transforms(
|
72
|
-
%
|
73
|
-
%
|
70
|
+
%(We heard it said:\n\n> ""Today the coalition is remedying those deficiencies by putting in place a new fast track process where the people's elected representatives have responsibility for the final decisions about Britain's future instead of unelected commissioners."") =>
|
71
|
+
%(We heard it said:\n\n> Today the coalition is remedying those deficiencies by putting in place a new fast track process where the people's elected representatives have responsibility for the final decisions about Britain's future instead of unelected commissioners.),
|
74
72
|
)
|
75
73
|
assert_remover_transforms(
|
76
|
-
%
|
77
|
-
%
|
74
|
+
%(> ""Today the coalition is remedying those deficiencies by putting in place a new fast track process where the people's elected representatives have responsibility for the final decisions about Britain's future instead of unelected commissioners.) =>
|
75
|
+
%(> Today the coalition is remedying those deficiencies by putting in place a new fast track process where the people's elected representatives have responsibility for the final decisions about Britain's future instead of unelected commissioners.),
|
78
76
|
)
|
79
77
|
end
|
80
78
|
|
81
79
|
test "removes quotes correctly from multi-line blockquotes" do
|
82
80
|
assert_remover_transforms(
|
83
|
-
%
|
84
|
-
%
|
81
|
+
%(> "Here is a block quote using 2 lines and two of the arrows.\n> I am not sure how this will render. I think it will mash them together.") =>
|
82
|
+
%(> Here is a block quote using 2 lines and two of the arrows.\n> I am not sure how this will render. I think it will mash them together.),
|
85
83
|
)
|
86
84
|
end
|
87
85
|
|
88
86
|
test "preserves multiline blockquotes with plain newlines quotes" do
|
89
87
|
assert_remover_transforms(
|
90
|
-
%
|
91
|
-
%
|
92
|
-
|
88
|
+
%(> "line 1\n> \n> "line 2\n> \n> "line 3") =>
|
89
|
+
%(> line 1\n> \n> line 2\n> \n> line 3),
|
90
|
+
)
|
93
91
|
end
|
94
92
|
|
95
93
|
test "preserve newlines when there's a blockquote with additional text after" do
|
96
|
-
assert_leaves_untouched(%
|
94
|
+
assert_leaves_untouched(%(> \n> blah))
|
97
95
|
end
|
98
96
|
end
|