ffi (~> 1.15)
ffi-compiler (~> 1.0)
ast (2.4.2)
- autoprefixer-rails (10.4.16.0)
+ autoprefixer-rails (10.4.19.0)
execjs (~> 2)
aws-eventstream (1.3.0)
- aws-partitions (1.959.0)
+ aws-partitions (1.962.0)
aws-sdk-core (3.201.3)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sdk-kms (1.88.0)
aws-sdk-core (~> 3, >= 3.201.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.156.0)
+ aws-sdk-s3 (1.157.0)
aws-sdk-core (~> 3, >= 3.201.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
bigdecimal (3.1.8)
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
- bootsnap (1.18.3)
+ bootsnap (1.18.4)
msgpack (~> 1.2)
bootstrap (5.3.3)
autoprefixer-rails (>= 9.1.0)
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
- erb_lint (0.5.0)
+ erb_lint (0.6.0)
activesupport
better_html (>= 2.0.1)
parser (>= 2.7.1.4)
rainbow
- rubocop
+ rubocop (>= 1)
smart_properties
erubi (1.13.0)
execjs (2.9.1)
factory_bot_rails (6.4.3)
factory_bot (~> 6.4)
railties (>= 5.0.0)
- faraday (2.10.0)
+ faraday (2.10.1)
faraday-net_http (>= 2.0, < 3.2)
logger
faraday-net_http (3.1.1)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (3.25.4)
- hashdiff (1.1.0)
+ hashdiff (1.1.1)
hashie (5.0.0)
highline (3.1.0)
reline
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
- rexml (3.3.3)
+ rexml (3.3.4)
strscan
rinku (2.0.6)
rotp (6.3.0)
rouge (4.3.0)
rtlcss (0.2.1)
mini_racer (>= 0.6.3)
- rubocop (1.65.0)
+ rubocop (1.65.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.31.3)
+ rubocop-ast (1.32.0)
parser (>= 3.3.1.0)
rubocop-capybara (2.21.0)
rubocop (~> 1.41)
sprockets-exporters_pack (0.1.2)
brotli (>= 0.2.0)
sprockets (>= 4.0.0.beta3)
- sprockets-rails (3.5.1)
+ sprockets-rails (3.5.2)
actionpack (>= 6.1)
activesupport (>= 6.1)
sprockets (>= 3.0.0)
if @entry
@title = t ".title", :user => params[:display_name], :title => @entry.title
@og_image = @entry.body.image
+ @og_image_alt = @entry.body.image_alt
@comments = can?(:unhide, DiaryComment) ? @entry.comments : @entry.visible_comments
else
@title = t "diary_entries.no_such_entry.title", :id => params[:id]
module OpenGraphHelper
require "addressable/uri"
- def opengraph_tags(title = nil, og_image = nil)
+ def opengraph_tags(title = nil, og_image = nil, og_image_alt = nil)
tags = {
"og:site_name" => t("layouts.project_name.title"),
"og:title" => title || t("layouts.project_name.title"),
"og:type" => "website",
- "og:image" => og_image_url(og_image),
"og:url" => url_for(:only_path => false),
"og:description" => t("layouts.intro_text")
- }
+ }.merge(
+ opengraph_image_properties(og_image, og_image_alt)
+ )
safe_join(tags.map do |property, content|
tag.meta(:property => property, :content => content)
private
- def og_image_url(og_image)
+ def opengraph_image_properties(og_image, og_image_alt)
begin
- return Addressable::URI.join(root_url, og_image).normalize if og_image
+ if og_image
+ properties = {}
+ properties["og:image"] = Addressable::URI.join(root_url, og_image).normalize
+ properties["og:image:alt"] = og_image_alt if og_image_alt
+ return properties
+ end
rescue Addressable::URI::InvalidURIError
# return default image
end
- image_url("osm_logo_256.png")
+ {
+ "og:image" => image_url("osm_logo_256.png"),
+ "og:image:alt" => t("layouts.logo.alt_text")
+ }
end
end
json.partial! "api/root_attributes"
-json.changesets(@changesets) do |changeset|
- json.partial! changeset
+json.changesets do
+ json.array! @changesets, :partial => "changeset", :as => :changeset
end
json.partial! "api/root_attributes"
-json.messages(@messages) do |message|
- json.partial! message
+json.messages do
+ json.array! @messages, :partial => "message", :as => :message
end
json.partial! "api/root_attributes"
-json.messages(@messages) do |message|
- json.partial! message
+json.messages do
+ json.array! @messages, :partial => "message", :as => :message
end
json.type "FeatureCollection"
-json.features(@notes) do |note|
- json.partial! note
+json.features do
+ json.array! @notes, :partial => "note", :as => :note
end
json.partial! "api/root_attributes"
-json.users(@users) do |user|
- json.partial! user
+json.users do
+ json.array! @users, :partial => "user", :as => :user
end
<% end -%>
<%= tag.link :rel => "search", :type => "application/opensearchdescription+xml", :title => "OpenStreetMap Search", :href => asset_path("osm.xml") %>
<%= tag.meta :name => "description", :content => "OpenStreetMap is the free wiki world map." %>
-<%= opengraph_tags(@title, @og_image) %>
+<%= opengraph_tags(@title, @og_image, @og_image_alt) %>
<% if flash[:matomo_goal] -%>
<%= tag.meta :name => "matomo-goal", :content => flash[:matomo_goal] %>
<% end -%>
nil
end
+ def image_alt
+ nil
+ end
+
protected
def simple_format(text)
end
def image
- return @image if defined? @image
+ @image_element = first_image_element(document.root) unless defined? @image_element
+ @image_element.attr["src"] if @image_element
+ end
- @image = first_image_element(document.root)&.attr&.[]("src")
+ def image_alt
+ @image_element = first_image_element(document.root) unless defined? @image_element
+ @image_element.attr["alt"] if @image_element
end
private
OauthToken.where("invalidated_at < NOW() - INTERVAL '28 days'").delete_all
RequestToken.where("authorized_at IS NULL AND created_at < NOW() - INTERVAL '28 days'").delete_all
+Doorkeeper::AccessGrant.where("revoked_at < NOW() - INTERVAL '28 days' OR (created_at + expires_in * INTERVAL '1 second') < NOW() - INTERVAL '28 days'").delete_all
+Doorkeeper::AccessToken.where("revoked_at < NOW() - INTERVAL '28 days' OR (created_at + expires_in * INTERVAL '1 second') < NOW() - INTERVAL '28 days'").delete_all
+
exit 0
"can't upload a simple valid creation to changeset: #{@response.body}"
# check the returned payload
- assert_select "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
- assert_select "diffResult>node", 1
- assert_select "diffResult>way", 1
- assert_select "diffResult>relation", 1
-
- # inspect the response to find out what the new element IDs are
- doc = XML::Parser.string(@response.body).parse
- new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
- new_way_id = doc.find("//diffResult/way").first["new_id"].to_i
- new_rel_id = doc.find("//diffResult/relation").first["new_id"].to_i
-
- # check the old IDs are all present and negative one
- assert_equal(-1, doc.find("//diffResult/node").first["old_id"].to_i)
- assert_equal(-1, doc.find("//diffResult/way").first["old_id"].to_i)
- assert_equal(-1, doc.find("//diffResult/relation").first["old_id"].to_i)
-
- # check the versions are present and equal one
- assert_equal 1, doc.find("//diffResult/node").first["new_version"].to_i
- assert_equal 1, doc.find("//diffResult/way").first["new_version"].to_i
- assert_equal 1, doc.find("//diffResult/relation").first["new_version"].to_i
+ new_node_id, new_way_id, new_rel_id = nil
+ assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
+ # inspect the response to find out what the new element IDs are
+ # check the old IDs are all present and negative one
+ # check the versions are present and equal one
+ assert_dom "> node", 1 do |(node_el)|
+ new_node_id = node_el["new_id"].to_i
+ assert_dom "> @old_id", "-1"
+ assert_dom "> @new_version", "1"
+ end
+ assert_dom "> way", 1 do |(way_el)|
+ new_way_id = way_el["new_id"].to_i
+ assert_dom "> @old_id", "-1"
+ assert_dom "> @new_version", "1"
+ end
+ assert_dom "> relation", 1 do |(rel_el)|
+ new_rel_id = rel_el["new_id"].to_i
+ assert_dom "> @old_id", "-1"
+ assert_dom "> @new_version", "1"
+ end
+ end
# check that the changes made it into the database
assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
"can't do a conditional delete of in use objects: #{@response.body}"
# check the returned payload
- assert_select "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
- assert_select "diffResult>node", 1
- assert_select "diffResult>way", 1
- assert_select "diffResult>relation", 1
-
- # parse the response
- doc = XML::Parser.string(@response.body).parse
-
- # check the old IDs are all present and what we expect
- assert_equal used_node.id, doc.find("//diffResult/node").first["old_id"].to_i
- assert_equal used_way.id, doc.find("//diffResult/way").first["old_id"].to_i
- assert_equal used_relation.id, doc.find("//diffResult/relation").first["old_id"].to_i
-
- # check the new IDs are all present and unchanged
- assert_equal used_node.id, doc.find("//diffResult/node").first["new_id"].to_i
- assert_equal used_way.id, doc.find("//diffResult/way").first["new_id"].to_i
- assert_equal used_relation.id, doc.find("//diffResult/relation").first["new_id"].to_i
-
- # check the new versions are all present and unchanged
- assert_equal used_node.version, doc.find("//diffResult/node").first["new_version"].to_i
- assert_equal used_way.version, doc.find("//diffResult/way").first["new_version"].to_i
- assert_equal used_relation.version, doc.find("//diffResult/relation").first["new_version"].to_i
+ assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
+ # check the old IDs are all present and what we expect
+ # check the new IDs are all present and unchanged
+ # check the new versions are all present and unchanged
+ assert_dom "> node", 1 do
+ assert_dom "> @old_id", used_node.id.to_s
+ assert_dom "> @new_id", used_node.id.to_s
+ assert_dom "> @new_version", used_node.version.to_s
+ end
+ assert_dom "> way", 1 do
+ assert_dom "> @old_id", used_way.id.to_s
+ assert_dom "> @new_id", used_way.id.to_s
+ assert_dom "> @new_version", used_way.version.to_s
+ end
+ assert_dom "> relation", 1 do
+ assert_dom "> @old_id", used_relation.id.to_s
+ assert_dom "> @new_id", used_relation.id.to_s
+ assert_dom "> @new_version", used_relation.version.to_s
+ end
+ end
# check that nothing was, in fact, deleted
assert Node.find(used_node.id).visible
"can't upload a complex diff to changeset: #{@response.body}"
# check the returned payload
- assert_select "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
- assert_select "diffResult>node", 1
- assert_select "diffResult>way", 1
- assert_select "diffResult>relation", 1
-
- # inspect the response to find out what the new element IDs are
- doc = XML::Parser.string(@response.body).parse
- new_node_id = doc.find("//diffResult/node").first["new_id"].to_i
+ new_node_id = nil
+ assert_dom "diffResult[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
+ assert_dom "> node", 1 do |(node_el)|
+ new_node_id = node_el["new_id"].to_i
+ end
+ assert_dom "> way", 1
+ assert_dom "> relation", 1
+ end
# check that the changes made it into the database
assert_equal 2, Node.find(new_node_id).tags.size, "new node should have two tags"
get changesets_path(:bbox => "-10,-10, 10, 10")
assert_response :success, "can't get changesets in bbox"
- assert_changesets [changeset2, changeset3]
+ assert_changesets_in_order [changeset3, changeset2]
get changesets_path(:bbox => "4.5,4.5,4.6,4.6")
assert_response :success, "can't get changesets in bbox"
- assert_changesets [changeset3]
+ assert_changesets_in_order [changeset3]
# not found when looking for changesets of non-existing users
get changesets_path(:user => User.maximum(:id) + 1)
auth_header = basic_authorization_header private_user.email, "test"
get changesets_path(:user => private_user.id), :headers => auth_header
assert_response :success, "can't get changesets by user ID"
- assert_changesets [private_user_changeset, private_user_closed_changeset]
+ assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
get changesets_path(:display_name => private_user.display_name), :headers => auth_header
assert_response :success, "can't get changesets by user name"
- assert_changesets [private_user_changeset, private_user_closed_changeset]
+ assert_changesets_in_order [private_user_changeset, private_user_closed_changeset]
# test json endpoint
get changesets_path(:display_name => private_user.display_name), :headers => auth_header, :params => { :format => "json" }
get changesets_path(:user => private_user.id, :open => true), :headers => auth_header
assert_response :success, "can't get changesets by user and open"
- assert_changesets [private_user_changeset]
+ assert_changesets_in_order [private_user_changeset]
get changesets_path(:time => "2007-12-31"), :headers => auth_header
assert_response :success, "can't get changesets by time-since"
- assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
+ assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
get changesets_path(:time => "2008-01-01T12:34Z"), :headers => auth_header
assert_response :success, "can't get changesets by time-since with hour"
- assert_changesets [private_user_changeset, private_user_closed_changeset, changeset, closed_changeset, changeset2, changeset3]
+ assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset, private_user_closed_changeset, closed_changeset]
get changesets_path(:time => "2007-12-31T23:59Z,2008-01-02T00:01Z"), :headers => auth_header
assert_response :success, "can't get changesets by time-range"
- assert_changesets [closed_changeset]
+ assert_changesets_in_order [closed_changeset]
get changesets_path(:open => "true"), :headers => auth_header
assert_response :success, "can't get changesets by open-ness"
- assert_changesets [private_user_changeset, changeset, changeset2, changeset3]
+ assert_changesets_in_order [changeset3, changeset2, changeset, private_user_changeset]
get changesets_path(:closed => "true"), :headers => auth_header
assert_response :success, "can't get changesets by closed-ness"
- assert_changesets [private_user_closed_changeset, closed_changeset]
+ assert_changesets_in_order [private_user_closed_changeset, closed_changeset]
get changesets_path(:closed => "true", :user => private_user.id), :headers => auth_header
assert_response :success, "can't get changesets by closed-ness and user"
- assert_changesets [private_user_closed_changeset]
+ assert_changesets_in_order [private_user_closed_changeset]
get changesets_path(:closed => "true", :user => user.id), :headers => auth_header
assert_response :success, "can't get changesets by closed-ness and user"
- assert_changesets [closed_changeset]
+ assert_changesets_in_order [closed_changeset]
get changesets_path(:changesets => "#{private_user_changeset.id},#{changeset.id},#{closed_changeset.id}"), :headers => auth_header
assert_response :success, "can't get changesets by id (as comma-separated string)"
- assert_changesets [private_user_changeset, changeset, closed_changeset]
+ assert_changesets_in_order [changeset, private_user_changeset, closed_changeset]
get changesets_path(:changesets => ""), :headers => auth_header
assert_response :bad_request, "should be a bad request since changesets is empty"
end
end
- ##
- # check that certain changesets exist in the output
- def assert_changesets(changesets)
- assert_select "osm>changeset", changesets.size
- changesets.each do |changeset|
- assert_select "osm>changeset[id='#{changeset.id}']", 1
- end
- end
-
##
# check that certain changesets exist in the output in the specified order
def assert_changesets_in_order(changesets)
# tests whether the API works and prevents incorrect use while trying
# to update nodes.
def test_update
+ invalid_attr_values = [["lat", 91.0], ["lat", -91.0], ["lon", 181.0], ["lon", -181.0]]
+
## First test with no user credentials
# try and update a node without authorisation
# first try to delete node without auth
assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
## try and submit invalid updates
- xml = xml_attr_rewrite(xml_for_node(private_node), "lat", 91.0)
- put api_node_path(private_node), :params => xml.to_s, :headers => auth_header
- assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
-
- xml = xml_attr_rewrite(xml_for_node(private_node), "lat", -91.0)
- put api_node_path(private_node), :params => xml.to_s, :headers => auth_header
- assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
-
- xml = xml_attr_rewrite(xml_for_node(private_node), "lon", 181.0)
- put api_node_path(private_node), :params => xml.to_s, :headers => auth_header
- assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
-
- xml = xml_attr_rewrite(xml_for_node(private_node), "lon", -181.0)
- put api_node_path(private_node), :params => xml.to_s, :headers => auth_header
- assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
+ invalid_attr_values.each do |name, value|
+ xml = xml_attr_rewrite(xml_for_node(private_node), name, value)
+ put api_node_path(private_node), :params => xml.to_s, :headers => auth_header
+ assert_require_public_data "node at #{name}=#{value} should be forbidden, when data isn't public"
+ end
## finally, produce a good request which still won't work
xml = xml_for_node(private_node)
assert_response :conflict, "update with changeset=0 should be rejected"
## try and submit invalid updates
- xml = xml_attr_rewrite(xml_for_node(node), "lat", 91.0)
- put api_node_path(node), :params => xml.to_s, :headers => auth_header
- assert_response :bad_request, "node at lat=91 should be rejected"
-
- xml = xml_attr_rewrite(xml_for_node(node), "lat", -91.0)
- put api_node_path(node), :params => xml.to_s, :headers => auth_header
- assert_response :bad_request, "node at lat=-91 should be rejected"
-
- xml = xml_attr_rewrite(xml_for_node(node), "lon", 181.0)
- put api_node_path(node), :params => xml.to_s, :headers => auth_header
- assert_response :bad_request, "node at lon=181 should be rejected"
-
- xml = xml_attr_rewrite(xml_for_node(node), "lon", -181.0)
- put api_node_path(node), :params => xml.to_s, :headers => auth_header
- assert_response :bad_request, "node at lon=-181 should be rejected"
+ invalid_attr_values.each do |name, value|
+ xml = xml_attr_rewrite(xml_for_node(node), name, value)
+ put api_node_path(node), :params => xml.to_s, :headers => auth_header
+ assert_response :bad_request, "node at #{name}=#{value} should be rejected"
+ end
## next, attack the versioning
current_node_version = node.version
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", ActionController::Base.helpers.image_url("osm_logo_256.png", :host => root_url)
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "OpenStreetMap logo"
+ end
end
def test_show_og_image
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", "https://example.com/picture.jpg"
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "some picture"
+ end
end
def test_show_og_image_with_relative_uri
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", "#{root_url}picture.jpg"
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "some local picture"
+ end
end
def test_show_og_image_with_spaces
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", "https://example.com/the%20picture.jpg"
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "some picture"
+ end
end
def test_show_og_image_with_relative_uri_and_spaces
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", "#{root_url}the%20picture.jpg"
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "some local picture"
+ end
end
def test_show_og_image_with_invalid_uri
assert_dom "head meta[property='og:image']" do
assert_dom "> @content", ActionController::Base.helpers.image_url("osm_logo_256.png", :host => root_url)
end
+ assert_dom "head meta[property='og:image:alt']" do
+ assert_dom "> @content", "OpenStreetMap logo"
+ end
+ end
+
+ def test_show_og_image_without_alt
+ user = create(:user)
+ diary_entry = create(:diary_entry, :user => user, :body => "<img src='https://example.com/no_alt.gif'>")
+
+ get diary_entry_path(user, diary_entry)
+ assert_response :success
+ assert_dom "head meta[property='og:image']" do
+ assert_dom "> @content", "https://example.com/no_alt.gif"
+ end
+ assert_dom "head meta[property='og:image:alt']", :count => 0
end
def test_hide
def test_text_no_image
r = RichText.new("text", "foo https://example.com/ bar")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_html_no_image
r = RichText.new("html", "foo <a href='https://example.com/'>bar</a> baz")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_markdown_no_image
r = RichText.new("markdown", "foo [bar](https://example.com/) baz")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_markdown_image
r = RichText.new("markdown", "foo ![bar](https://example.com/image.jpg) baz")
assert_equal "https://example.com/image.jpg", r.image
+ assert_equal "bar", r.image_alt
end
def test_markdown_first_image
r = RichText.new("markdown", "foo ![bar1](https://example.com/image1.jpg) baz\nfoo ![bar2](https://example.com/image2.jpg) baz")
assert_equal "https://example.com/image1.jpg", r.image
+ assert_equal "bar1", r.image_alt
end
def test_markdown_image_with_empty_src
r = RichText.new("markdown", "![invalid]()")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_markdown_skip_image_with_empty_src
r = RichText.new("markdown", "![invalid]() ![valid](https://example.com/valid.gif)")
assert_equal "https://example.com/valid.gif", r.image
+ assert_equal "valid", r.image_alt
end
def test_markdown_html_image
+ r = RichText.new("markdown", "<img src='https://example.com/img_element.png' alt='alt text here'>")
+ assert_equal "https://example.com/img_element.png", r.image
+ assert_equal "alt text here", r.image_alt
+ end
+
+ def test_markdown_html_image_without_alt
r = RichText.new("markdown", "<img src='https://example.com/img_element.png'>")
assert_equal "https://example.com/img_element.png", r.image
+ assert_nil r.image_alt
end
def test_markdown_html_image_with_empty_src
- r = RichText.new("markdown", "<img src=''>")
+ r = RichText.new("markdown", "<img src='' alt='forgot src'>")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_markdown_skip_html_image_with_empty_src
- r = RichText.new("markdown", "<img src=''> <img src='https://example.com/next_img_element.png'>")
+ r = RichText.new("markdown", "<img src='' alt='forgot src'> <img src='https://example.com/next_img_element.png' alt='have src'>")
assert_equal "https://example.com/next_img_element.png", r.image
+ assert_equal "have src", r.image_alt
end
def test_markdown_html_image_without_src
- r = RichText.new("markdown", "<img>")
+ r = RichText.new("markdown", "<img alt='totally forgot src'>")
assert_nil r.image
+ assert_nil r.image_alt
end
def test_markdown_skip_html_image_without_src
- r = RichText.new("markdown", "<img> <img src='https://example.com/next_img_element.png'>")
+ r = RichText.new("markdown", "<img alt='totally forgot src'> <img src='https://example.com/next_img_element.png' alt='have src'>")
assert_equal "https://example.com/next_img_element.png", r.image
+ assert_equal "have src", r.image_alt
end
private