govspeak 6.5.11 → 6.7.2
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 +21 -0
- data/README.md +21 -4
- data/lib/govspeak/header_extractor.rb +3 -2
- data/lib/govspeak/html_sanitizer.rb +5 -4
- data/lib/govspeak/post_processor.rb +1 -1
- data/lib/govspeak/presenters/attachment_presenter.rb +2 -1
- data/lib/govspeak/presenters/contact_presenter.rb +12 -12
- data/lib/govspeak/version.rb +1 -1
- data/lib/govspeak.rb +78 -10
- data/lib/templates/contact.html.erb +1 -1
- data/test/govspeak_attachment_test.rb +2 -2
- data/test/govspeak_attachments_image_test.rb +1 -1
- data/test/govspeak_button_test.rb +8 -24
- data/test/govspeak_contacts_test.rb +3 -3
- data/test/govspeak_footnote_test.rb +4 -4
- data/test/govspeak_images_bang_test.rb +7 -27
- data/test/govspeak_images_test.rb +9 -34
- data/test/govspeak_test.rb +213 -1
- data/test/govspeak_test_helper.rb +7 -1
- data/test/html_sanitizer_test.rb +6 -0
- data/test/test_helper.rb +4 -4
- metadata +20 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3edb8b047b41d9c15581ec77b6b4ba829dae69f64729265deaf5cad25b91744a
|
4
|
+
data.tar.gz: 2e899be9ea70576afb234804213e8697278483a77118dc5f03857bdf7ca0e49d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8779eb8e734e660d3ce9b7e020f18c14792ba1488fb92f59d23455e0cf5474f2ded6d376bd3c25e0b09c7e27362a5e56a271bf1f3084ce60a8cc355d07a97b34
|
7
|
+
data.tar.gz: 569414d843883df41a9bd9abbc286b94667e50b0156321468d01008980fd28863ef65e127a422a7e55c4793b649901000a6a474e0450903a801287880fa75af6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
## Unreleased
|
2
|
+
|
3
|
+
* Minimum Ruby version specified at 2.6 [215](https://github.com/alphagov/govspeak/pull/215)
|
4
|
+
|
5
|
+
## 6.7.2
|
6
|
+
|
7
|
+
* Fix footnotes in legislative lists [216](https://github.com/alphagov/govspeak/pull/216)
|
8
|
+
|
9
|
+
## 6.7.1
|
10
|
+
|
11
|
+
* Update failing test [212](https://github.com/alphagov/govspeak/pull/212)
|
12
|
+
* Fix stats headline HTML semantics [213](https://github.com/alphagov/govspeak/pull/213)
|
13
|
+
|
14
|
+
## 6.7.0
|
15
|
+
|
16
|
+
* Update heading & docs [#206](https://github.com/alphagov/govspeak/pull/206)
|
17
|
+
|
18
|
+
## 6.6.0
|
19
|
+
|
20
|
+
* Allow passed elements to be relaxed from sanitization [#203](https://github.com/alphagov/govspeak/pull/203)
|
21
|
+
|
1
22
|
## 6.5.11
|
2
23
|
|
3
24
|
* Fix issue rendering $CTA blocks before $C (PR#202)
|
data/README.md
CHANGED
@@ -18,6 +18,23 @@ then create a new document
|
|
18
18
|
doc = Govspeak::Document.new "^Test^"
|
19
19
|
puts doc.to_html
|
20
20
|
|
21
|
+
## Changes appearing across GOV.UK
|
22
|
+
|
23
|
+
Some additional steps or considerations are needed to ensure changes to govspeak cascade across GOV.UK in a holistic way.
|
24
|
+
|
25
|
+
Once govspeak has been updated and version incremented then:
|
26
|
+
- [`govuk_publishing_components` govspeak](https://components.publishing.service.gov.uk/component-guide/govspeak) will also need updating to reflect your most recent change.
|
27
|
+
- [Publishing apps](https://docs.publishing.service.gov.uk/apps.html) (including but not limited to [content-publisher](https://github.com/alphagov/content-publisher) & [whitehall](https://github.com/alphagov/whitehall)) also use govspeak, these apps will need to be released with the new govspeak version present.
|
28
|
+
|
29
|
+
Also, consider if:
|
30
|
+
- [whitehall](https://github.com/alphagov/whitehall) needs updating (as custom govspeak changes are present)
|
31
|
+
- [govuk-content-schema](https://github.com/alphagov/govuk-content-schemas) needs updating
|
32
|
+
- [govpspeak-preview](https://github.com/alphagov/govspeak-preview) is worth updating
|
33
|
+
|
34
|
+
Any pages that use govspeak to generate Content will need to *republished* in order for the new changes to be reflected.
|
35
|
+
|
36
|
+
- Data Labs can help identify which pages need updating by [submitting a request](https://gov-uk.atlassian.net/wiki/spaces/GOVUK/pages/1860075525/GOV.UK+Data+Labs#Submitting-a-data-science-request) and [#govuk-2ndline](https://docs.publishing.service.gov.uk/manual/2nd-line.html) can help with republishing
|
37
|
+
|
21
38
|
# Extensions
|
22
39
|
|
23
40
|
In addition to the [standard Markdown syntax](http://daringfireball.net/projects/markdown/syntax "Markdown syntax"), we have added our own extensions.
|
@@ -106,9 +123,9 @@ Statistic headlines highlight important numbers in content. Displays a statistic
|
|
106
123
|
Creates the following:
|
107
124
|
|
108
125
|
```html
|
109
|
-
<
|
126
|
+
<div class="stat-headline">
|
110
127
|
<p><em>13.8bn</em> years since the big bang</p>
|
111
|
-
</
|
128
|
+
</div>
|
112
129
|
```
|
113
130
|
|
114
131
|
## Points of Contact
|
@@ -598,8 +615,8 @@ will output
|
|
598
615
|
```html
|
599
616
|
<div id="contact_123" class="contact">
|
600
617
|
<div class="content">
|
601
|
-
<h3>Government Digital Service</h3>
|
602
618
|
<div class="vcard contact-inner">
|
619
|
+
<p>Government Digital Service</p>
|
603
620
|
<div class="email-url-number">
|
604
621
|
<p class="email">
|
605
622
|
<span class="type">Email</span>
|
@@ -676,4 +693,4 @@ which outputs
|
|
676
693
|
Start Now
|
677
694
|
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg>
|
678
695
|
</a>
|
679
|
-
```
|
696
|
+
```
|
@@ -30,9 +30,10 @@ module Govspeak
|
|
30
30
|
def find_headers(parent)
|
31
31
|
headers = []
|
32
32
|
|
33
|
-
|
33
|
+
case parent.type
|
34
|
+
when :header
|
34
35
|
headers << build_header(parent)
|
35
|
-
|
36
|
+
when :html_element
|
36
37
|
parent.children.each do |child|
|
37
38
|
if child.type == :header
|
38
39
|
headers << build_header(child)
|
@@ -40,18 +40,19 @@ class Govspeak::HtmlSanitizer
|
|
40
40
|
@allowed_image_hosts = options[:allowed_image_hosts]
|
41
41
|
end
|
42
42
|
|
43
|
-
def sanitize
|
43
|
+
def sanitize(allowed_elements: [])
|
44
44
|
transformers = [TableCellTextAlignWhitelister.new]
|
45
45
|
if @allowed_image_hosts && @allowed_image_hosts.any?
|
46
46
|
transformers << ImageSourceWhitelister.new(@allowed_image_hosts)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
|
+
Sanitize.clean(@dirty_html, Sanitize::Config.merge(sanitize_config(allowed_elements: allowed_elements), transformers: transformers))
|
49
50
|
end
|
50
51
|
|
51
|
-
def sanitize_config
|
52
|
+
def sanitize_config(allowed_elements: [])
|
52
53
|
Sanitize::Config.merge(
|
53
54
|
Sanitize::Config::RELAXED,
|
54
|
-
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path],
|
55
|
+
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path].concat(allowed_elements),
|
55
56
|
attributes: {
|
56
57
|
:all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
|
57
58
|
"a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data],
|
@@ -126,7 +126,7 @@ module Govspeak
|
|
126
126
|
el.content = "[footnote #{footnote_number}]"
|
127
127
|
end
|
128
128
|
document.css("[role='doc-backlink']").map do |el|
|
129
|
-
backlink_number = "
|
129
|
+
backlink_number = " #{el.css('sup')[0].content}" if el.css("sup")[0].present?
|
130
130
|
el["aria-label"] = "go to where this is referenced#{backlink_number}"
|
131
131
|
end
|
132
132
|
end
|
@@ -4,6 +4,7 @@ require "htmlentities"
|
|
4
4
|
module Govspeak
|
5
5
|
class AttachmentPresenter
|
6
6
|
attr_reader :attachment
|
7
|
+
|
7
8
|
include ActionView::Helpers::TagHelper
|
8
9
|
include ActionView::Helpers::NumberHelper
|
9
10
|
include ActionView::Helpers::TextHelper
|
@@ -25,7 +26,7 @@ module Govspeak
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def file_extension
|
28
|
-
#
|
29
|
+
# NOTE: this is a separate parameter rather than being calculated from the
|
29
30
|
# filename because at the time of writing not all apps were using the effects
|
30
31
|
# of this field.
|
31
32
|
attachment[:file_extension]
|
@@ -23,30 +23,30 @@ module Govspeak
|
|
23
23
|
|
24
24
|
def post_addresses
|
25
25
|
@post_addresses ||= begin
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
addresses = contact.dig(:details, :post_addresses) || []
|
27
|
+
filter_post_addresses(addresses)
|
28
|
+
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def email_addresses
|
32
32
|
@email_addresses ||= begin
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
emails = contact.dig(:details, :email_addresses) || []
|
34
|
+
emails.select { |e| e[:email].present? }
|
35
|
+
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def phone_numbers
|
39
39
|
@phone_numbers ||= begin
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
phone_numbers = contact.dig(:details, :phone_numbers) || []
|
41
|
+
phone_numbers.select { |p| p[:number].present? }
|
42
|
+
end
|
43
43
|
end
|
44
44
|
|
45
45
|
def contact_form_links
|
46
46
|
@contact_form_links ||= begin
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
contact_form_links = contact.dig(:details, :contact_form_links) || []
|
48
|
+
contact_form_links.select { |c| c[:link].present? }
|
49
|
+
end
|
50
50
|
end
|
51
51
|
|
52
52
|
private
|
data/lib/govspeak/version.rb
CHANGED
data/lib/govspeak.rb
CHANGED
@@ -53,6 +53,7 @@ module Govspeak
|
|
53
53
|
@source = source ? source.dup : ""
|
54
54
|
|
55
55
|
@images = options.delete(:images) || []
|
56
|
+
@allowed_elements = options.delete(:allowed_elements) || []
|
56
57
|
@attachments = Array.wrap(options.delete(:attachments))
|
57
58
|
@links = Array.wrap(options.delete(:links))
|
58
59
|
@contacts = Array.wrap(options.delete(:contacts))
|
@@ -61,18 +62,30 @@ module Govspeak
|
|
61
62
|
sanitize: true,
|
62
63
|
syntax_highlighter: nil }.merge(options)
|
63
64
|
@options[:entity_output] = :symbolic
|
65
|
+
@footnote_definition_html = nil
|
66
|
+
@acronyms = []
|
64
67
|
end
|
65
68
|
|
66
69
|
def to_html
|
67
70
|
@to_html ||= begin
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
html = if @options[:sanitize]
|
72
|
+
HtmlSanitizer.new(kramdown_doc.to_html).sanitize(allowed_elements: @allowed_elements)
|
73
|
+
else
|
74
|
+
kramdown_doc.to_html
|
75
|
+
end
|
76
|
+
|
77
|
+
unless @footnote_definition_html.nil?
|
78
|
+
regex = /<div class="footnotes".*[<\/div>]/m
|
79
|
+
|
80
|
+
if html.scan(regex).empty?
|
81
|
+
html << @footnote_definition_html
|
82
|
+
else
|
83
|
+
html.gsub!(regex, @footnote_definition_html)
|
84
|
+
end
|
85
|
+
end
|
73
86
|
|
74
|
-
|
75
|
-
|
87
|
+
Govspeak::PostProcessor.process(html, self)
|
88
|
+
end
|
76
89
|
end
|
77
90
|
|
78
91
|
def to_liquid
|
@@ -109,6 +122,9 @@ module Govspeak
|
|
109
122
|
def preprocess(source)
|
110
123
|
source = Govspeak::BlockquoteExtraQuoteRemover.remove(source)
|
111
124
|
source = remove_forbidden_characters(source)
|
125
|
+
|
126
|
+
legislative_list_footnote_definitions(source)
|
127
|
+
|
112
128
|
self.class.extensions.each do |_, regexp, block|
|
113
129
|
source.gsub!(regexp) do
|
114
130
|
instance_exec(*Regexp.last_match.captures, &block)
|
@@ -117,6 +133,40 @@ module Govspeak
|
|
117
133
|
source
|
118
134
|
end
|
119
135
|
|
136
|
+
def legislative_list_footnote_definitions(source)
|
137
|
+
is_legislative_list = source.scan(/\$LegislativeList.*?\[\^\d\]*.*?\$EndLegislativeList/m).size.positive?
|
138
|
+
footnotes = source.scan(/\[\^(\d)\]:(.*)/)
|
139
|
+
@acronyms = source.scan(/(?<=\*)\[(.*)\]:(.*)/)
|
140
|
+
|
141
|
+
if is_legislative_list && footnotes.size.positive?
|
142
|
+
list_items = footnotes.map do |footnote|
|
143
|
+
number = footnote[0]
|
144
|
+
text = footnote[1].strip
|
145
|
+
footnote_definition = Govspeak::Document.new(text).to_html[/(?<=<p>).*(?=<\/p>)/]
|
146
|
+
|
147
|
+
<<~HTML_SNIPPET
|
148
|
+
<li id="fn:#{number}" role="doc-endnote">
|
149
|
+
<p>
|
150
|
+
#{footnote_definition}<a href="#fnref:#{number}" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
151
|
+
</p>
|
152
|
+
</li>
|
153
|
+
HTML_SNIPPET
|
154
|
+
end
|
155
|
+
|
156
|
+
@footnote_definition_html = <<~HTML_CONTAINER
|
157
|
+
<div class="footnotes" role="doc-endnotes">
|
158
|
+
<ol>
|
159
|
+
#{list_items.join.strip}
|
160
|
+
</ol>
|
161
|
+
</div>
|
162
|
+
HTML_CONTAINER
|
163
|
+
end
|
164
|
+
|
165
|
+
unless @footnote_definition_html.nil? && @acronyms.size.positive?
|
166
|
+
add_acronym_alt_text(@footnote_definition_html)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
120
170
|
def remove_forbidden_characters(source)
|
121
171
|
# These are characters that are not deemed not suitable for
|
122
172
|
# markup: https://www.w3.org/TR/unicode-xml/#Charlist
|
@@ -160,7 +210,7 @@ module Govspeak
|
|
160
210
|
([^)]+) # capture inside of link text markdown
|
161
211
|
\) # match end of link text markdown
|
162
212
|
\s* # any whitespace between opening bracket and link
|
163
|
-
{
|
213
|
+
{/button} # match ending bracket
|
164
214
|
(?:\r|\n|$) # non-capturing match to make sure end of line and linebreak
|
165
215
|
}x) do |attributes, text, href|
|
166
216
|
button_classes = "govuk-button"
|
@@ -184,8 +234,8 @@ module Govspeak
|
|
184
234
|
end
|
185
235
|
|
186
236
|
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m) do |body|
|
187
|
-
%(\n\n<
|
188
|
-
#{Govspeak::Document.new(body.strip).to_html}</
|
237
|
+
%(\n\n<div class="stat-headline">
|
238
|
+
#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
189
239
|
end
|
190
240
|
|
191
241
|
# FIXME: these surrounded_by arguments look dodgy
|
@@ -294,6 +344,18 @@ module Govspeak
|
|
294
344
|
doc.gsub!("<ul>", "<ol>")
|
295
345
|
doc.gsub!("</ul>", "</ol>")
|
296
346
|
doc.sub!("<ol>", '<ol class="legislative-list">')
|
347
|
+
|
348
|
+
footnotes = body.scan(/\[\^(\d+)\]/).flatten
|
349
|
+
|
350
|
+
footnotes.each do |footnote|
|
351
|
+
html = "<sup id=\"fnref:#{footnote}\" role=\"doc-noteref\">" \
|
352
|
+
"<a href=\"#fn:#{footnote}\" class=\"footnote\" rel=\"footnote\">" \
|
353
|
+
"[footnote #{footnote}]</a></sup>"
|
354
|
+
|
355
|
+
doc.sub!(/(\[\^#{footnote}\])/, html)
|
356
|
+
end
|
357
|
+
|
358
|
+
add_acronym_alt_text(doc) if @acronyms.size.positive?
|
297
359
|
end
|
298
360
|
end
|
299
361
|
end
|
@@ -385,6 +447,12 @@ module Govspeak
|
|
385
447
|
def encode(text)
|
386
448
|
HTMLEntities.new.encode(text)
|
387
449
|
end
|
450
|
+
|
451
|
+
def add_acronym_alt_text(html)
|
452
|
+
@acronyms.each do |acronym|
|
453
|
+
html.gsub!(acronym[0], "<abbr title=\"#{acronym[1].strip}\">#{acronym[0]}</abbr>")
|
454
|
+
end
|
455
|
+
end
|
388
456
|
end
|
389
457
|
end
|
390
458
|
|
@@ -4,8 +4,8 @@
|
|
4
4
|
%>
|
5
5
|
<div id="contact_<%= contact.content_id %>" class="<%= ['contact', extra_class].flatten.join(' ') %>">
|
6
6
|
<div class="content">
|
7
|
-
<h3><%= contact.title %></h3>
|
8
7
|
<div class="vcard contact-inner">
|
8
|
+
<p><%= contact.title %></p>
|
9
9
|
<% contact.post_addresses.each do |address| %>
|
10
10
|
<%= Govspeak::HCardPresenter.new(address).render %>
|
11
11
|
<% end %>
|
@@ -17,7 +17,7 @@ class GovspeakAttachmentTest < Minitest::Test
|
|
17
17
|
}
|
18
18
|
|
19
19
|
rendered = render_govspeak("[Attachment:attachment.pdf]", [attachment])
|
20
|
-
assert_match(/<section class="gem-c-attachment
|
20
|
+
assert_match(/<section class="gem-c-attachment/, rendered)
|
21
21
|
assert_match(/Attachment Title/, rendered)
|
22
22
|
end
|
23
23
|
|
@@ -32,7 +32,7 @@ class GovspeakAttachmentTest < Minitest::Test
|
|
32
32
|
assert_equal("<p>some text [Attachment:attachment.pdf]</p>\n", rendered)
|
33
33
|
|
34
34
|
rendered = render_govspeak("[Attachment:attachment.pdf] some text", [attachment])
|
35
|
-
assert_match(/<section class="gem-c-attachment
|
35
|
+
assert_match(/<section class="gem-c-attachment/, rendered)
|
36
36
|
assert_match(/<p>some text<\/p>/, rendered)
|
37
37
|
end
|
38
38
|
end
|
@@ -7,34 +7,34 @@ class GovspeakTest < Minitest::Test
|
|
7
7
|
include GovspeakTestHelper
|
8
8
|
|
9
9
|
test_given_govspeak "{button start cross-domain-tracking:UA-23066786-5}[Start now](https://www.registertovote.service.gov.uk/register-to-vote/start){/button}" do
|
10
|
-
|
10
|
+
assert_html_selector 'a.gem-c-button.govuk-button--start[data-module="cross-domain-tracking"][data-tracking-code="UA-23066786-5"][href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
11
11
|
assert_text_output "Start now"
|
12
12
|
end
|
13
13
|
|
14
14
|
# The same as above but with line breaks
|
15
15
|
test_given_govspeak "{button start cross-domain-tracking:UA-23066786-5}\n\n\n[Start now](https://www.registertovote.service.gov.uk/register-to-vote/start)\n\n\n{/button}" do
|
16
|
-
|
16
|
+
assert_html_selector 'a.gem-c-button.govuk-button--start[data-module="cross-domain-tracking"][data-tracking-code="UA-23066786-5"][href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
17
17
|
assert_text_output "Start now"
|
18
18
|
end
|
19
19
|
|
20
20
|
test_given_govspeak "{button cross-domain-tracking:UA-23066786-5}[Start now](https://www.registertovote.service.gov.uk/register-to-vote/start){/button}" do
|
21
|
-
|
21
|
+
assert_html_selector 'a.gem-c-button:not(.govuk-button--start)[data-module="cross-domain-tracking"][data-tracking-code="UA-23066786-5"][href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
22
22
|
assert_text_output "Start now"
|
23
23
|
end
|
24
24
|
|
25
25
|
test_given_govspeak "{button start}[Start now](https://www.registertovote.service.gov.uk/register-to-vote/start){/button}" do
|
26
|
-
|
26
|
+
assert_html_selector 'a.gem-c-button.govuk-button--start[href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
27
27
|
assert_text_output "Start now"
|
28
28
|
end
|
29
29
|
|
30
30
|
test_given_govspeak "{button}[Start now](https://www.registertovote.service.gov.uk/register-to-vote/start){/button}" do
|
31
|
-
|
31
|
+
assert_html_selector 'a.gem-c-button:not(.govuk-button--start)[href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
32
32
|
assert_text_output "Start now"
|
33
33
|
end
|
34
34
|
|
35
35
|
# Test other text outputs
|
36
36
|
test_given_govspeak "{button}[Something else](https://www.registertovote.service.gov.uk/register-to-vote/start){/button}" do
|
37
|
-
|
37
|
+
assert_html_selector 'a.gem-c-button[href="https://www.registertovote.service.gov.uk/register-to-vote/start"]'
|
38
38
|
assert_text_output "Something else"
|
39
39
|
end
|
40
40
|
|
@@ -54,22 +54,6 @@ class GovspeakTest < Minitest::Test
|
|
54
54
|
assert_text_output "Text before the button with line breaks Start Now test after the button"
|
55
55
|
end
|
56
56
|
|
57
|
-
# Test README examples
|
58
|
-
test_given_govspeak "{button}[Continue](https://gov.uk/random){/button}" do
|
59
|
-
assert_html_output '<p><a class="gem-c-button govuk-button" role="button" href="https://gov.uk/random">Continue</a></p>'
|
60
|
-
assert_text_output "Continue"
|
61
|
-
end
|
62
|
-
|
63
|
-
test_given_govspeak "{button start}[Start Now](https://gov.uk/random){/button}" do
|
64
|
-
assert_html_output '<p><a class="gem-c-button govuk-button govuk-button--start" role="button" href="https://gov.uk/random"> Start Now <svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewbox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg></a></p>'
|
65
|
-
assert_text_output "Start Now"
|
66
|
-
end
|
67
|
-
|
68
|
-
test_given_govspeak "{button start cross-domain-tracking:UA-XXXXXX-Y}[Start Now](https://example.com/external-service/start-now){/button}" do
|
69
|
-
assert_html_output '<p><a class="gem-c-button govuk-button govuk-button--start" role="button" data-module="cross-domain-tracking" data-tracking-code="UA-XXXXXX-Y" data-tracking-name="govspeakButtonTracker" href="https://example.com/external-service/start-now"> Start Now <svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewbox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg></a></p>'
|
70
|
-
assert_text_output "Start Now"
|
71
|
-
end
|
72
|
-
|
73
57
|
# Test indenting button govspeak results in no render, useful in guides
|
74
58
|
test_given_govspeak " {button start cross-domain-tracking:UA-XXXXXX-Y}[Example](https://example.com/external-service/start-now){/button}" do
|
75
59
|
assert_html_output %{
|
@@ -107,7 +91,7 @@ class GovspeakTest < Minitest::Test
|
|
107
91
|
lorem lorem lorem
|
108
92
|
lorem lorem lorem
|
109
93
|
|
110
|
-
{button
|
94
|
+
{button}[Random page](https://gov.uk/random){/button}
|
111
95
|
|
112
96
|
lorem lorem lorem
|
113
97
|
lorem lorem lorem
|
@@ -120,7 +104,7 @@ class GovspeakTest < Minitest::Test
|
|
120
104
|
<p>lorem lorem lorem
|
121
105
|
lorem lorem lorem</p>
|
122
106
|
|
123
|
-
<p><a class="gem-c-button govuk-button
|
107
|
+
<p><a class="gem-c-button govuk-button" role="button" href="https://gov.uk/random">Random page</a></p>
|
124
108
|
|
125
109
|
<p>lorem lorem lorem
|
126
110
|
lorem lorem lorem</p>
|
@@ -51,7 +51,7 @@ class GovspeakContactsTest < Minitest::Test
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def compress_html(html)
|
54
|
-
html.gsub(/[\n\r]
|
54
|
+
html.gsub(/[\n\r]+\s*/, "")
|
55
55
|
end
|
56
56
|
|
57
57
|
test "contact is rendered when present in options[:contacts]" do
|
@@ -62,8 +62,8 @@ class GovspeakContactsTest < Minitest::Test
|
|
62
62
|
expected_html_output = %(
|
63
63
|
<div id="contact_4f3383e4-48a2-4461-a41d-f85ea8b89ba0" class="contact postal-address">
|
64
64
|
<div class="content">
|
65
|
-
<h3>Government Digital Service</h3>
|
66
65
|
<div class="vcard contact-inner">
|
66
|
+
<p>Government Digital Service</p>
|
67
67
|
<p class="adr">
|
68
68
|
<span class="street-address">125 Kingsway</span><br>
|
69
69
|
<span class="locality">Holborn</span><br>
|
@@ -106,8 +106,8 @@ class GovspeakContactsTest < Minitest::Test
|
|
106
106
|
expected_html_output = %(
|
107
107
|
<div id="contact_4f3383e4-48a2-4461-a41d-f85ea8b89ba0" class="contact">
|
108
108
|
<div class="content">
|
109
|
-
<h3>Government Digital Service</h3>
|
110
109
|
<div class="vcard contact-inner">
|
110
|
+
<p>Government Digital Service</p>
|
111
111
|
<div class="email-url-number">
|
112
112
|
<p class="email">
|
113
113
|
<span class="type">Email</span>
|
@@ -20,13 +20,13 @@ class GovspeakFootnoteTest < Minitest::Test
|
|
20
20
|
[^3]: And then they both point here." do
|
21
21
|
assert_html_output(
|
22
22
|
%(
|
23
|
-
<p>Footnotes can be added<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote">[footnote 1]</a></sup>.</p>
|
23
|
+
<p>Footnotes can be added<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup>.</p>
|
24
24
|
|
25
|
-
<p>Footnotes can be added too<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote">[footnote 2]</a></sup>.</p>
|
25
|
+
<p>Footnotes can be added too<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup>.</p>
|
26
26
|
|
27
|
-
<p>This footnote has a reference number<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote">[footnote 3]</a></sup>.</p>
|
27
|
+
<p>This footnote has a reference number<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">[footnote 3]</a></sup>.</p>
|
28
28
|
|
29
|
-
<p>And this footnote has the same reference number<sup id="fnref:3:1" role="doc-noteref"><a href="#fn:3" class="footnote">[footnote 3]</a></sup>.</p>
|
29
|
+
<p>And this footnote has the same reference number<sup id="fnref:3:1" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">[footnote 3]</a></sup>.</p>
|
30
30
|
|
31
31
|
<div class="footnotes" role="doc-endnotes">
|
32
32
|
<ol>
|
@@ -18,9 +18,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
18
18
|
test "!!n syntax renders an image in options[:images]" do
|
19
19
|
given_govspeak "!!1", images: [Image.new] do
|
20
20
|
assert_html_output(
|
21
|
-
|
22
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
23
|
-
%(</figure>),
|
21
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
24
22
|
)
|
25
23
|
end
|
26
24
|
end
|
@@ -28,9 +26,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
28
26
|
test "!!n syntax escapes alt text" do
|
29
27
|
given_govspeak "!!1", images: [Image.new(alt_text: %(my alt '&"<>))] do
|
30
28
|
assert_html_output(
|
31
|
-
|
32
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt '&"<>"></div>) +
|
33
|
-
%(</figure>),
|
29
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt '&"<>\"></div></figure>",
|
34
30
|
)
|
35
31
|
end
|
36
32
|
end
|
@@ -48,10 +44,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
48
44
|
test "!!n syntax adds image caption if given" do
|
49
45
|
given_govspeak "!!1", images: [Image.new(caption: "My Caption & so on")] do
|
50
46
|
assert_html_output(
|
51
|
-
|
52
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
53
|
-
%(<figcaption><p>My Caption & so on</p></figcaption>) +
|
54
|
-
%(</figure>),
|
47
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>My Caption & so on</p></figcaption></figure>",
|
55
48
|
)
|
56
49
|
end
|
57
50
|
end
|
@@ -59,9 +52,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
59
52
|
test "!!n syntax ignores a blank caption" do
|
60
53
|
given_govspeak "!!1", images: [Image.new(caption: " ")] do
|
61
54
|
assert_html_output(
|
62
|
-
|
63
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
64
|
-
%(</figure>),
|
55
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
65
56
|
)
|
66
57
|
end
|
67
58
|
end
|
@@ -69,10 +60,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
69
60
|
test "¡¡n syntax adds image credit if given" do
|
70
61
|
given_govspeak "!!1", images: [Image.new(credit: "My Credit & so on")] do
|
71
62
|
assert_html_output(
|
72
|
-
|
73
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
74
|
-
%(<figcaption><p>Image credit: My Credit & so on</p></figcaption>) +
|
75
|
-
%(</figure>),
|
63
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>Image credit: My Credit & so on</p></figcaption></figure>",
|
76
64
|
)
|
77
65
|
end
|
78
66
|
end
|
@@ -80,9 +68,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
80
68
|
test "!!n syntax ignores a blank credit" do
|
81
69
|
given_govspeak "!!1", images: [Image.new(credit: " ")] do
|
82
70
|
assert_html_output(
|
83
|
-
|
84
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
85
|
-
%(</figure>),
|
71
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
86
72
|
)
|
87
73
|
end
|
88
74
|
end
|
@@ -90,13 +76,7 @@ class GovspeakImagesBangTest < Minitest::Test
|
|
90
76
|
test "!!n syntax adds image caption and credit if given" do
|
91
77
|
given_govspeak "!!1", images: [Image.new(caption: "My Caption & so on", credit: "My Credit & so on")] do
|
92
78
|
assert_html_output(
|
93
|
-
|
94
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
95
|
-
%(<figcaption>) +
|
96
|
-
%(<p>My Caption & so on</p>\n) +
|
97
|
-
%(<p>Image credit: My Credit & so on</p>) +
|
98
|
-
%(</figcaption>) +
|
99
|
-
%(</figure>),
|
79
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>My Caption & so on</p>\n<p>Image credit: My Credit & so on</p></figcaption></figure>",
|
100
80
|
)
|
101
81
|
end
|
102
82
|
end
|
@@ -14,9 +14,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
14
14
|
test "Image:image-id syntax renders an image in options[:images]" do
|
15
15
|
given_govspeak "[Image:image-id]", images: [build_image] do
|
16
16
|
assert_html_output(
|
17
|
-
|
18
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
19
|
-
%(</figure>),
|
17
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
20
18
|
)
|
21
19
|
end
|
22
20
|
end
|
@@ -24,9 +22,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
24
22
|
test "Image:image-id syntax escapes alt text" do
|
25
23
|
given_govspeak "[Image:image-id]", images: [build_image(alt_text: %(my alt '&"<>))] do
|
26
24
|
assert_html_output(
|
27
|
-
|
28
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt '&"<>"></div>) +
|
29
|
-
%(</figure>),
|
25
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt '&"<>\"></div></figure>",
|
30
26
|
)
|
31
27
|
end
|
32
28
|
end
|
@@ -39,10 +35,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
39
35
|
test "Image:image-id syntax adds image caption if given" do
|
40
36
|
given_govspeak "[Image:image-id]", images: [build_image(caption: "My Caption & so on")] do
|
41
37
|
assert_html_output(
|
42
|
-
|
43
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
44
|
-
%(<figcaption><p>My Caption & so on</p></figcaption>) +
|
45
|
-
%(</figure>),
|
38
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>My Caption & so on</p></figcaption></figure>",
|
46
39
|
)
|
47
40
|
end
|
48
41
|
end
|
@@ -50,9 +43,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
50
43
|
test "Image:image-id syntax ignores a blank caption" do
|
51
44
|
given_govspeak "[Image:image-id]", images: [build_image(caption: " ")] do
|
52
45
|
assert_html_output(
|
53
|
-
|
54
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
55
|
-
%(</figure>),
|
46
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
56
47
|
)
|
57
48
|
end
|
58
49
|
end
|
@@ -60,10 +51,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
60
51
|
test "Image:image-id syntax adds image credit if given" do
|
61
52
|
given_govspeak "[Image:image-id]", images: [build_image(credit: "My Credit & so on")] do
|
62
53
|
assert_html_output(
|
63
|
-
|
64
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
65
|
-
%(<figcaption><p>Image credit: My Credit & so on</p></figcaption>) +
|
66
|
-
%(</figure>),
|
54
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>Image credit: My Credit & so on</p></figcaption></figure>",
|
67
55
|
)
|
68
56
|
end
|
69
57
|
end
|
@@ -71,9 +59,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
71
59
|
test "Image:image-id syntax ignores a blank credit" do
|
72
60
|
given_govspeak "[Image:image-id]", images: [build_image(credit: " ")] do
|
73
61
|
assert_html_output(
|
74
|
-
|
75
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
76
|
-
%(</figure>),
|
62
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
77
63
|
)
|
78
64
|
end
|
79
65
|
end
|
@@ -81,13 +67,7 @@ class GovspeakImagesTest < Minitest::Test
|
|
81
67
|
test "Image:image-id syntax adds image caption and credit if given" do
|
82
68
|
given_govspeak "[Image:image-id]", images: [build_image(caption: "My Caption & so on", credit: "My Credit & so on")] do
|
83
69
|
assert_html_output(
|
84
|
-
|
85
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>\n) +
|
86
|
-
%(<figcaption>) +
|
87
|
-
%(<p>My Caption & so on</p>\n) +
|
88
|
-
%(<p>Image credit: My Credit & so on</p>) +
|
89
|
-
%(</figcaption>) +
|
90
|
-
%(</figure>),
|
70
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div>\n<figcaption><p>My Caption & so on</p>\n<p>Image credit: My Credit & so on</p></figcaption></figure>",
|
91
71
|
)
|
92
72
|
end
|
93
73
|
end
|
@@ -99,18 +79,13 @@ class GovspeakImagesTest < Minitest::Test
|
|
99
79
|
|
100
80
|
given_govspeak "[Image:image-id]", images: [build_image] do
|
101
81
|
assert_html_output(
|
102
|
-
|
103
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
104
|
-
%(</figure>),
|
82
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
|
105
83
|
)
|
106
84
|
end
|
107
85
|
|
108
86
|
given_govspeak "[Image:image-id] some text", images: [build_image] do
|
109
87
|
assert_html_output(
|
110
|
-
|
111
|
-
%(<div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div>) +
|
112
|
-
%(</figure>\n) +
|
113
|
-
%(<p>some text</p>),
|
88
|
+
"<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>\n<p>some text</p>",
|
114
89
|
)
|
115
90
|
end
|
116
91
|
end
|
data/test/govspeak_test.rb
CHANGED
@@ -30,7 +30,7 @@ class GovspeakTest < Minitest::Test
|
|
30
30
|
|
31
31
|
test "stat-headline block extension" do
|
32
32
|
rendered = Govspeak::Document.new("this \n{stat-headline}*13.8bn* Age of the universe in years{/stat-headline}").to_html
|
33
|
-
assert_equal %(<p>this</p>\n\n<
|
33
|
+
assert_equal %(<p>this</p>\n\n<div class="stat-headline">\n<p><em>13.8bn</em> Age of the universe in years</p>\n</div>\n), rendered
|
34
34
|
end
|
35
35
|
|
36
36
|
test "extracts headers with text, level and generated id" do
|
@@ -591,6 +591,213 @@ Teston
|
|
591
591
|
}
|
592
592
|
end
|
593
593
|
|
594
|
+
test_given_govspeak "
|
595
|
+
$LegislativeList
|
596
|
+
* 1. Item 1[^1]
|
597
|
+
* 2. Item 2[^2]
|
598
|
+
* 3. Item 3
|
599
|
+
$EndLegislativeList
|
600
|
+
|
601
|
+
[^1]: Footnote definition one
|
602
|
+
[^2]: Footnote definition two
|
603
|
+
" do
|
604
|
+
assert_html_output %(
|
605
|
+
<ol class="legislative-list">
|
606
|
+
<li>1. Item 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup>
|
607
|
+
</li>
|
608
|
+
<li>2. Item 2<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup>
|
609
|
+
</li>
|
610
|
+
<li>3. Item 3</li>
|
611
|
+
</ol>
|
612
|
+
|
613
|
+
<div class="footnotes" role="doc-endnotes">
|
614
|
+
<ol>
|
615
|
+
<li id="fn:1" role="doc-endnote">
|
616
|
+
<p>
|
617
|
+
Footnote definition one<a href="#fnref:1" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
618
|
+
</p>
|
619
|
+
</li>
|
620
|
+
<li id="fn:2" role="doc-endnote">
|
621
|
+
<p>
|
622
|
+
Footnote definition two<a href="#fnref:2" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
623
|
+
</p>
|
624
|
+
</li>
|
625
|
+
</ol>
|
626
|
+
</div>
|
627
|
+
)
|
628
|
+
end
|
629
|
+
|
630
|
+
test_given_govspeak "
|
631
|
+
$LegislativeList
|
632
|
+
* 1. Item 1[^1]
|
633
|
+
* 2. Item 2
|
634
|
+
* 3. Item 3
|
635
|
+
$EndLegislativeList
|
636
|
+
|
637
|
+
This is a paragraph with a footnote[^2].
|
638
|
+
|
639
|
+
$LegislativeList
|
640
|
+
* 1. Item 1
|
641
|
+
* 2. Item 2[^3]
|
642
|
+
* 3. Item 3
|
643
|
+
$EndLegislativeList
|
644
|
+
|
645
|
+
[^1]: Footnote definition one
|
646
|
+
[^2]: Footnote definition two
|
647
|
+
[^3]: Footnote definition two
|
648
|
+
" do
|
649
|
+
assert_html_output %(
|
650
|
+
<ol class="legislative-list">
|
651
|
+
<li>1. Item 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup>
|
652
|
+
</li>
|
653
|
+
<li>2. Item 2</li>
|
654
|
+
<li>3. Item 3</li>
|
655
|
+
</ol>
|
656
|
+
|
657
|
+
<p>This is a paragraph with a footnote<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup>.</p>
|
658
|
+
|
659
|
+
<ol class="legislative-list">
|
660
|
+
<li>1. Item 1</li>
|
661
|
+
<li>2. Item 2<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">[footnote 3]</a></sup>
|
662
|
+
</li>
|
663
|
+
<li>3. Item 3</li>
|
664
|
+
</ol>
|
665
|
+
|
666
|
+
<div class="footnotes" role="doc-endnotes">
|
667
|
+
<ol>
|
668
|
+
<li id="fn:1" role="doc-endnote">
|
669
|
+
<p>
|
670
|
+
Footnote definition one<a href="#fnref:1" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
671
|
+
</p>
|
672
|
+
</li>
|
673
|
+
<li id="fn:2" role="doc-endnote">
|
674
|
+
<p>
|
675
|
+
Footnote definition two<a href="#fnref:2" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
676
|
+
</p>
|
677
|
+
</li>
|
678
|
+
<li id="fn:3" role="doc-endnote">
|
679
|
+
<p>
|
680
|
+
Footnote definition two<a href="#fnref:3" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
681
|
+
</p>
|
682
|
+
</li>
|
683
|
+
</ol>
|
684
|
+
</div>
|
685
|
+
)
|
686
|
+
end
|
687
|
+
|
688
|
+
test_given_govspeak "
|
689
|
+
$LegislativeList
|
690
|
+
* 1. Item 1[^1] with a [link](http://www.gov.uk)
|
691
|
+
* 2. Item 2
|
692
|
+
* 3. Item 3
|
693
|
+
$EndLegislativeList
|
694
|
+
|
695
|
+
This is a paragraph with a footnote[^2]
|
696
|
+
|
697
|
+
[^1]: Footnote definition one
|
698
|
+
[^2]: Footnote definition two
|
699
|
+
" do
|
700
|
+
assert_html_output %(
|
701
|
+
<ol class="legislative-list">
|
702
|
+
<li>1. Item 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup> with a <a href="http://www.gov.uk">link</a>
|
703
|
+
</li>
|
704
|
+
<li>2. Item 2</li>
|
705
|
+
<li>3. Item 3</li>
|
706
|
+
</ol>
|
707
|
+
|
708
|
+
<p>This is a paragraph with a footnote<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup></p>
|
709
|
+
|
710
|
+
<div class="footnotes" role="doc-endnotes">
|
711
|
+
<ol>
|
712
|
+
<li id="fn:1" role="doc-endnote">
|
713
|
+
<p>
|
714
|
+
Footnote definition one<a href="#fnref:1" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
715
|
+
</p>
|
716
|
+
</li>
|
717
|
+
<li id="fn:2" role="doc-endnote">
|
718
|
+
<p>
|
719
|
+
Footnote definition two<a href="#fnref:2" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
720
|
+
</p>
|
721
|
+
</li>
|
722
|
+
</ol>
|
723
|
+
</div>
|
724
|
+
)
|
725
|
+
end
|
726
|
+
|
727
|
+
test_given_govspeak "
|
728
|
+
$LegislativeList
|
729
|
+
* 1. Item 1[^1] with a [link](http://www.gov.uk)
|
730
|
+
* 2. Item 2
|
731
|
+
* 3. Item 3[^2]
|
732
|
+
$EndLegislativeList
|
733
|
+
|
734
|
+
[^1]: Footnote definition one with a [link](http://www.gov.uk) included
|
735
|
+
[^2]: Footnote definition two with an external [link](http://www.google.com)
|
736
|
+
" do
|
737
|
+
assert_html_output %(
|
738
|
+
<ol class="legislative-list">
|
739
|
+
<li>1. Item 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup> with a <a href="http://www.gov.uk">link</a>
|
740
|
+
</li>
|
741
|
+
<li>2. Item 2</li>
|
742
|
+
<li>3. Item 3<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup>
|
743
|
+
</li>
|
744
|
+
</ol>
|
745
|
+
|
746
|
+
<div class="footnotes" role="doc-endnotes">
|
747
|
+
<ol>
|
748
|
+
<li id="fn:1" role="doc-endnote">
|
749
|
+
<p>
|
750
|
+
Footnote definition one with a <a href="http://www.gov.uk">link</a> included<a href="#fnref:1" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
751
|
+
</p>
|
752
|
+
</li>
|
753
|
+
<li id="fn:2" role="doc-endnote">
|
754
|
+
<p>
|
755
|
+
Footnote definition two with an external <a rel="external" href="http://www.google.com">link</a><a href="#fnref:2" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
756
|
+
</p>
|
757
|
+
</li>
|
758
|
+
</ol>
|
759
|
+
</div>
|
760
|
+
)
|
761
|
+
end
|
762
|
+
|
763
|
+
test_given_govspeak "
|
764
|
+
$LegislativeList
|
765
|
+
* 1. Item 1[^1] with an ACRONYM
|
766
|
+
* 2. Item 2[^2]
|
767
|
+
* 3. Item 3
|
768
|
+
$EndLegislativeList
|
769
|
+
|
770
|
+
[^1]: Footnote definition one
|
771
|
+
[^2]: Footnote definition two with an ACRONYM
|
772
|
+
|
773
|
+
*[ACRONYM]: This is the acronym explanation
|
774
|
+
" do
|
775
|
+
assert_html_output %(
|
776
|
+
<ol class="legislative-list">
|
777
|
+
<li>1. Item 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">[footnote 1]</a></sup> with an <abbr title="This is the acronym explanation">ACRONYM</abbr>
|
778
|
+
</li>
|
779
|
+
<li>2. Item 2<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">[footnote 2]</a></sup>
|
780
|
+
</li>
|
781
|
+
<li>3. Item 3</li>
|
782
|
+
</ol>
|
783
|
+
|
784
|
+
<div class="footnotes" role="doc-endnotes">
|
785
|
+
<ol>
|
786
|
+
<li id="fn:1" role="doc-endnote">
|
787
|
+
<p>
|
788
|
+
Footnote definition one<a href="#fnref:1" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
789
|
+
</p>
|
790
|
+
</li>
|
791
|
+
<li id="fn:2" role="doc-endnote">
|
792
|
+
<p>
|
793
|
+
Footnote definition two with an <abbr title="This is the acronym explanation">ACRONYM</abbr><a href="#fnref:2" class="reversefootnote" role="doc-backlink" aria-label="go to where this is referenced">↩</a>
|
794
|
+
</p>
|
795
|
+
</li>
|
796
|
+
</ol>
|
797
|
+
</div>
|
798
|
+
)
|
799
|
+
end
|
800
|
+
|
594
801
|
test_given_govspeak "
|
595
802
|
The quick brown
|
596
803
|
$LegislativeList
|
@@ -666,6 +873,11 @@ Teston
|
|
666
873
|
assert_equal "<script>doGoodThings();</script>", document.to_html.strip
|
667
874
|
end
|
668
875
|
|
876
|
+
test "it can exclude stipulated elements from sanitization" do
|
877
|
+
document = Govspeak::Document.new("<uncommon-element>some content</uncommon-element>", allowed_elements: %w[uncommon-element])
|
878
|
+
assert_equal "<uncommon-element>some content</uncommon-element>", document.to_html.strip
|
879
|
+
end
|
880
|
+
|
669
881
|
test "identifies a Govspeak document containing malicious HTML as invalid" do
|
670
882
|
document = Govspeak::Document.new("<script>doBadThings();</script>")
|
671
883
|
refute document.valid?
|
@@ -26,6 +26,12 @@ module GovspeakTestHelper
|
|
26
26
|
@testcase.assert expected.strip == actual, describe_error(@govspeak, expected.strip, actual)
|
27
27
|
end
|
28
28
|
|
29
|
+
def assert_html_selector(selector)
|
30
|
+
html = document.to_html
|
31
|
+
fragment = Nokogiri::HTML.fragment(html)
|
32
|
+
@testcase.assert fragment.css(selector).any?, "Expected to find #{selector} within #{html}"
|
33
|
+
end
|
34
|
+
|
29
35
|
def remove_indentation(raw)
|
30
36
|
lines = raw.split("\n")
|
31
37
|
if lines.first.empty?
|
@@ -33,7 +39,7 @@ module GovspeakTestHelper
|
|
33
39
|
nonblanks = lines.reject { |l| l.match(/^ *$/) }
|
34
40
|
indentation = nonblanks.map { |line| line.match(/^ */)[0].size }.min
|
35
41
|
unindented = lines.map do |line|
|
36
|
-
line[indentation
|
42
|
+
line[indentation..]
|
37
43
|
end
|
38
44
|
unindented.join "\n"
|
39
45
|
else
|
data/test/html_sanitizer_test.rb
CHANGED
@@ -96,4 +96,10 @@ class HtmlSanitizerTest < Minitest::Test
|
|
96
96
|
assert_equal "<table><thead><tr><th>thing</th></tr></thead><tbody><tr><td>thing</td></tr></tbody></table>", Govspeak::HtmlSanitizer.new(html).sanitize
|
97
97
|
end
|
98
98
|
end
|
99
|
+
|
100
|
+
test "excludes specified elements from sanitization" do
|
101
|
+
html = "<custom-allowed-element><p>text</p></custom-allowed-element>"
|
102
|
+
assert_equal "<p>text</p>", Govspeak::HtmlSanitizer.new(html).sanitize
|
103
|
+
assert_equal html, Govspeak::HtmlSanitizer.new(html).sanitize(allowed_elements: %w[custom-allowed-element])
|
104
|
+
end
|
99
105
|
end
|
data/test/test_helper.rb
CHANGED
@@ -17,10 +17,10 @@ class Minitest::Test
|
|
17
17
|
clean_name = name.gsub(/\s+/, "_")
|
18
18
|
method = "test_#{clean_name.gsub(/\s+/, '_')}".to_sym
|
19
19
|
already_defined = begin
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
instance_method(method)
|
21
|
+
rescue StandardError
|
22
|
+
false
|
23
|
+
end
|
24
24
|
raise "#{method} exists" if already_defined
|
25
25
|
|
26
26
|
define_method(method, &block)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govspeak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -216,14 +216,14 @@ dependencies:
|
|
216
216
|
requirements:
|
217
217
|
- - "~>"
|
218
218
|
- !ruby/object:Gem::Version
|
219
|
-
version:
|
219
|
+
version: 4.0.0
|
220
220
|
type: :development
|
221
221
|
prerelease: false
|
222
222
|
version_requirements: !ruby/object:Gem::Requirement
|
223
223
|
requirements:
|
224
224
|
- - "~>"
|
225
225
|
- !ruby/object:Gem::Version
|
226
|
-
version:
|
226
|
+
version: 4.0.0
|
227
227
|
- !ruby/object:Gem::Dependency
|
228
228
|
name: simplecov
|
229
229
|
requirement: !ruby/object:Gem::Requirement
|
@@ -360,36 +360,36 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
360
360
|
requirements:
|
361
361
|
- - ">="
|
362
362
|
- !ruby/object:Gem::Version
|
363
|
-
version: '
|
363
|
+
version: '2.6'
|
364
364
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
365
365
|
requirements:
|
366
366
|
- - ">="
|
367
367
|
- !ruby/object:Gem::Version
|
368
368
|
version: '0'
|
369
369
|
requirements: []
|
370
|
-
rubygems_version: 3.
|
370
|
+
rubygems_version: 3.0.3
|
371
371
|
signing_key:
|
372
372
|
specification_version: 4
|
373
373
|
summary: Markup language for single domain
|
374
374
|
test_files:
|
375
|
-
- test/govspeak_images_bang_test.rb
|
376
|
-
- test/govspeak_attachment_test.rb
|
377
|
-
- test/govspeak_link_extractor_test.rb
|
378
|
-
- test/govspeak_attachments_inline_test.rb
|
379
|
-
- test/govspeak_button_test.rb
|
380
|
-
- test/govspeak_structured_headers_test.rb
|
381
375
|
- test/govspeak_attachments_image_test.rb
|
382
|
-
- test/
|
376
|
+
- test/govspeak_structured_headers_test.rb
|
377
|
+
- test/govspeak_images_bang_test.rb
|
378
|
+
- test/govspeak_link_test.rb
|
383
379
|
- test/govspeak_extract_contact_content_ids_test.rb
|
384
|
-
- test/
|
385
|
-
- test/govspeak_test.rb
|
386
|
-
- test/html_sanitizer_test.rb
|
387
|
-
- test/govspeak_footnote_test.rb
|
380
|
+
- test/govspeak_test_helper.rb
|
388
381
|
- test/blockquote_extra_quote_remover_test.rb
|
382
|
+
- test/govspeak_attachments_inline_test.rb
|
383
|
+
- test/govspeak_attachment_test.rb
|
389
384
|
- test/test_helper.rb
|
390
|
-
- test/
|
385
|
+
- test/govspeak_test.rb
|
386
|
+
- test/presenters/h_card_presenter_test.rb
|
387
|
+
- test/govspeak_footnote_test.rb
|
388
|
+
- test/govspeak_attachment_link_test.rb
|
389
|
+
- test/govspeak_link_extractor_test.rb
|
390
|
+
- test/govspeak_button_test.rb
|
391
391
|
- test/govspeak_images_test.rb
|
392
|
-
- test/govspeak_link_test.rb
|
393
392
|
- test/html_validator_test.rb
|
393
|
+
- test/html_sanitizer_test.rb
|
394
|
+
- test/govspeak_table_with_headers_test.rb
|
394
395
|
- test/govspeak_contacts_test.rb
|
395
|
-
- test/govspeak_test_helper.rb
|