Skip to content
Extraits de code Groupes Projets
Valider bcace2de rédigé par Denis Hovart's avatar Denis Hovart Validation de Jonne Haß
Parcourir les fichiers

6840 : meta tags update (#6998)

* Adds a new metadata helper and methods to PostPresenter to have metas on post pages.

* Adds tests to post controller to check correctness of metas

* Add methods to PersonPresenter to have metas on profile pages

* Correct meta data helper test

* Update PersonPresenter, add test to PeopleController

* Creates TagPresenter. Display tag metas on tag index page

* Updata meta data helper spec

* Not displaying bio as the description meta on profile page for now. Privacy concerns to be cleared.

* Set meta info as hashes in presenters

* Move original hardcoded metas info to config/defaults.yml

* metas_tags include by default the general metas, update views

* Update code style, clean views

* Renames TagPresenter StreamTagPresenter, updates TagController spec

* Add a default_metas entry to diaspora.yml.example

* Align metas hash in presenters, refactor meta data helper

* Use bio as description meta if user has a public profile

* Rename StreamTagPresenter to TagStreamPresenter
parent 96489e3c
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 296 ajouts et 128 suppressions
...@@ -69,27 +69,26 @@ class PeopleController < ApplicationController ...@@ -69,27 +69,26 @@ class PeopleController < ApplicationController
# renders the persons user profile page # renders the persons user profile page
def show def show
mark_corresponding_notifications_read if user_signed_in? mark_corresponding_notifications_read if user_signed_in?
@presenter = PersonPresenter.new(@person, current_user)
@person_json = PersonPresenter.new(@person, current_user).as_json
respond_to do |format| respond_to do |format|
format.all do format.all do
if user_signed_in? if user_signed_in?
@contact = current_user.contact_for(@person) @contact = current_user.contact_for(@person)
end end
gon.preloads[:person] = @person_json gon.preloads[:person] = @presenter.as_json
gon.preloads[:photos_count] = Photo.visible(current_user, @person).count(:all) gon.preloads[:photos_count] = Photo.visible(current_user, @person).count(:all)
gon.preloads[:contacts_count] = Contact.contact_contacts_for(current_user, @person).count(:all) gon.preloads[:contacts_count] = Contact.contact_contacts_for(current_user, @person).count(:all)
respond_with @person, layout: "with_header" respond_with @presenter, layout: "with_header"
end end
format.mobile do format.mobile do
@post_type = :all @post_type = :all
person_stream person_stream
respond_with @person respond_with @presenter
end end
format.json { render json: @person_json } format.json { render json: @presenter.as_json }
end end
end end
......
...@@ -20,13 +20,14 @@ class PhotosController < ApplicationController ...@@ -20,13 +20,14 @@ class PhotosController < ApplicationController
@post_type = :photos @post_type = :photos
@person = Person.find_by_guid(params[:person_id]) @person = Person.find_by_guid(params[:person_id])
authenticate_user! if @person.try(:remote?) && !user_signed_in? authenticate_user! if @person.try(:remote?) && !user_signed_in?
@presenter = PersonPresenter.new(@person, current_user)
if @person if @person
@contact = current_user.contact_for(@person) if user_signed_in? @contact = current_user.contact_for(@person) if user_signed_in?
@posts = Photo.visible(current_user, @person, :all, max_time) @posts = Photo.visible(current_user, @person, :all, max_time)
respond_to do |format| respond_to do |format|
format.all do format.all do
gon.preloads[:person] = PersonPresenter.new(@person, current_user).as_json gon.preloads[:person] = @presenter.as_json
gon.preloads[:photos_count] = Photo.visible(current_user, @person).count(:all) gon.preloads[:photos_count] = Photo.visible(current_user, @person).count(:all)
gon.preloads[:contacts_count] = Contact.contact_contacts_for(current_user, @person).count(:all) gon.preloads[:contacts_count] = Contact.contact_contacts_for(current_user, @person).count(:all)
render "people/show", layout: "with_header" render "people/show", layout: "with_header"
......
...@@ -19,14 +19,15 @@ class PostsController < ApplicationController ...@@ -19,14 +19,15 @@ class PostsController < ApplicationController
def show def show
post = post_service.find!(params[:id]) post = post_service.find!(params[:id])
post_service.mark_user_notifications(post.id) post_service.mark_user_notifications(post.id)
presenter = PostPresenter.new(post, current_user)
respond_to do |format| respond_to do |format|
format.html { format.html do
gon.post = PostPresenter.new(post, current_user) gon.post = presenter
render locals: {post: post} render locals: {post: presenter}
} end
format.mobile { render locals: {post: post} } format.mobile { render locals: {post: post} }
format.xml { render xml: DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(post)) } format.xml { render xml: DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(post)) }
format.json { render json: PostPresenter.new(post, current_user) } format.json { render json: presenter }
end end
end end
......
...@@ -37,9 +37,15 @@ class TagsController < ApplicationController ...@@ -37,9 +37,15 @@ class TagsController < ApplicationController
if user_signed_in? if user_signed_in?
gon.preloads[:tagFollowings] = tags gon.preloads[:tagFollowings] = tags
end end
@stream = Stream::Tag.new(current_user, params[:name], :max_time => max_time, :page => params[:page]) stream = Stream::Tag.new(current_user, params[:name], max_time: max_time, page: params[:page])
@stream = TagStreamPresenter.new(stream)
respond_with do |format| respond_with do |format|
format.json { render :json => @stream.stream_posts.map { |p| LastThreeCommentsDecorator.new(PostPresenter.new(p, current_user)) }} format.json do
posts = stream.stream_posts.map do |p|
LastThreeCommentsDecorator.new(PostPresenter.new(p, current_user))
end
render json: posts
end
end end
end end
......
module MetaDataHelper
include ActionView::Helpers::AssetUrlHelper
include ActionView::Helpers::TagHelper
def og_prefix
'og: http://ogp.me/ns# article: http://ogp.me/ns/article# profile: http://ogp.me/ns/profile#'
end
def site_url
AppConfig.environment.url
end
def default_image_url
asset_url "assets/branding/logos/asterisk.png"
end
def default_author_name
AppConfig.settings.pod_name
end
def default_description
AppConfig.settings.default_metas.description
end
def default_title
AppConfig.settings.default_metas.title
end
def general_metas
{
description: {name: "description", content: default_description},
og_description: {property: "description", content: default_description},
og_site_name: {property: "og:site_name", content: default_title},
og_url: {property: "og:url", content: site_url},
og_image: {property: "og:image", content: default_image_url},
og_type: {property: "og:type", content: "website"}
}
end
def metas_tags(attributes_list={}, with_general_metas=true)
attributes_list = general_metas.merge(attributes_list) if with_general_metas
attributes_list.map {|_, attributes| meta_tag attributes }.join("\n").html_safe
end
# recursively calls itself if attribute[:content] is an array
# (metas such as og:image or og:tag can be present multiple times with different values)
def meta_tag(attributes)
return "" if attributes.empty?
return tag(:meta, attributes) unless attributes[:content].respond_to?(:to_ary)
items = attributes.delete(:content)
items.map {|item| meta_tag(attributes.merge(content: item)) }.join("\n")
end
end
module OpenGraphHelper module OpenGraphHelper
def og_title(title)
meta_tag_with_property('og:title', title)
end
def og_type(post)
meta_tag_with_property('og:type', 'article')
end
def og_url(url)
meta_tag_with_property('og:url', url)
end
def og_image(post=nil)
tags = []
tags = post.photos.map{|x| meta_tag_with_property('og:image', x.url(:thumb_large))} if post
tags << meta_tag_with_property('og:image', default_image_url) if tags.empty?
tags.join(' ')
end
def og_description(description)
meta_tag_with_property('og:description', description)
end
def og_type(type='website')
meta_tag_with_property('og:type', type)
end
def og_namespace
AppConfig.services.facebook.open_graph_namespace
end
def og_site_name
meta_tag_with_property('og:site_name', AppConfig.settings.pod_name)
end
def og_common_tags
[og_site_name]
end
def og_general_tags
[
*og_common_tags,
og_type,
og_title('diaspora* social network'),
og_image,
og_url(AppConfig.environment.url),
og_description('diaspora* is the online social world where you are in control.')
].join("\n").html_safe
end
def og_page_post_tags(post)
tags = og_common_tags
if post.message
tags.concat [
*tags,
og_type("#{og_namespace}:frame"),
og_title(post_page_title(post, :length => 140)),
og_url(post_url(post)),
og_image(post),
og_description(post.message.plain_text_without_markdown truncate: 1000)
]
end
tags.join("\n").html_safe
end
def og_prefix
"og: http://ogp.me/ns# #{og_namespace}: https://diasporafoundation.org/ns/joindiaspora#"
end
def meta_tag_with_property(name, content)
tag(:meta, :property => name, :content => content)
end
def og_html(cache) def og_html(cache)
"<a href=\"#{cache.url}\" target=\"_blank\">" + "<a href=\"#{cache.url}\" target=\"_blank\">" +
" <div>" + " <div>" +
...@@ -91,14 +16,4 @@ module OpenGraphHelper ...@@ -91,14 +16,4 @@ module OpenGraphHelper
def oembed_image_tag(cache, prefix) def oembed_image_tag(cache, prefix)
image_tag(cache.data["#{prefix}url"], cache.options_hash(prefix)) image_tag(cache.data["#{prefix}url"], cache.options_hash(prefix))
end end
private
# This method compensates for hosting assets off of s3
def default_image_url
if image_path("branding/logos/asterisk.png").include?("http")
image_path("branding/logos/asterisk.png")
else
"#{root_url.chop}#{image_path('branding/logos/asterisk.png')}"
end
end
end end
class BasePresenter class BasePresenter
attr_reader :current_user attr_reader :current_user
include Rails.application.routes.url_helpers
class << self class << self
def new(*args) def new(*args)
...@@ -26,4 +27,10 @@ class BasePresenter ...@@ -26,4 +27,10 @@ class BasePresenter
nil nil
end end
end end
private
def default_url_options
{host: AppConfig.pod_uri.host, port: AppConfig.pod_uri.port}
end
end end
...@@ -25,6 +25,21 @@ class PersonPresenter < BasePresenter ...@@ -25,6 +25,21 @@ class PersonPresenter < BasePresenter
base_hash_with_contact.merge(profile: ProfilePresenter.new(profile).for_hovercard) base_hash_with_contact.merge(profile: ProfilePresenter.new(profile).for_hovercard)
end end
def metas_attributes
{
keywords: {name: "keywords", content: comma_separated_tags},
description: {name: "description", content: description},
og_title: {property: "og:title", content: title},
og_description: {property: "og:title", content: description},
og_url: {property: "og:url", content: url},
og_image: {property: "og:image", content: image_url},
og_type: {property: "og:type", content: "profile"},
og_profile_username: {property: "og:profile:username", content: name},
og_profile_firstname: {property: "og:profile:first_name", content: first_name},
og_profile_lastname: {property: "og:profile:last_name", content: last_name}
}
end
protected protected
def own_profile? def own_profile?
...@@ -88,4 +103,25 @@ class PersonPresenter < BasePresenter ...@@ -88,4 +103,25 @@ class PersonPresenter < BasePresenter
def is_blocked? def is_blocked?
current_user_person_block.present? current_user_person_block.present?
end end
def title
name
end
def comma_separated_tags
profile.tags.map(&:name).join(", ") if profile.tags
end
def url
url_for(@presentable)
end
def description
public_details? ? bio : ""
end
def image_url
return AppConfig.url_to @presentable.image_url if @presentable.image_url[0] == "/"
@presentable.image_url
end
end end
class PostPresenter < BasePresenter class PostPresenter < BasePresenter
include PostsHelper include PostsHelper
include MetaDataHelper
attr_accessor :post attr_accessor :post
def initialize(post, current_user=nil) def initialize(presentable, current_user=nil)
@post = post @post = presentable
@current_user = current_user super
end end
def as_json(_options={}) def as_json(_options={})
@post.as_json(only: directly_retrieved_attributes).merge(non_directly_retrieved_attributes) @post.as_json(only: directly_retrieved_attributes)
.merge(non_directly_retrieved_attributes)
end
def metas_attributes
{
keywords: {name: "keywords", content: comma_separated_tags},
description: {name: "description", content: description},
og_url: {property: "og:url", content: url},
og_title: {property: "og:title", content: title},
og_image: {property: "og:image", content: images},
og_description: {property: "og:description", content: description},
og_article_tag: {property: "og:article:tag", content: tags},
og_article_author: {property: "og:article:author", content: author_name},
og_article_modified: {property: "og:article:modified_time", content: modified_time_iso8601},
og_article_published: {property: "og:article:published_time", content: published_time_iso8601}
}
end
def page_title
post_page_title @post
end end
private private
...@@ -38,6 +59,10 @@ class PostPresenter < BasePresenter ...@@ -38,6 +59,10 @@ class PostPresenter < BasePresenter
} }
end end
def title
@post.message.present? ? @post.message.title : I18n.t("posts.presenter.title", name: @post.author_name)
end
def build_text def build_text
if @post.message if @post.message
@post.message.plain_text_for_json @post.message.plain_text_for_json
...@@ -58,10 +83,6 @@ class PostPresenter < BasePresenter ...@@ -58,10 +83,6 @@ class PostPresenter < BasePresenter
@post.photos.map {|p| p.as_api_response(:backbone) } @post.photos.map {|p| p.as_api_response(:backbone) }
end end
def title
@post.message.present? ? @post.message.title : I18n.t("posts.presenter.title", name: @post.author_name)
end
def root def root
if @post.respond_to?(:absolute_root) && @post.absolute_root.present? if @post.respond_to?(:absolute_root) && @post.absolute_root.present?
PostPresenter.new(@post.absolute_root, current_user).as_json PostPresenter.new(@post.absolute_root, current_user).as_json
...@@ -103,4 +124,34 @@ class PostPresenter < BasePresenter ...@@ -103,4 +124,34 @@ class PostPresenter < BasePresenter
def person def person
current_user.person current_user.person
end end
def images
photos.any? ? photos.map(&:url) : default_image_url
end
def published_time_iso8601
created_at.to_time.iso8601
end
def modified_time_iso8601
updated_at.to_time.iso8601
end
def tags
tags = @post.is_a?(Reshare) ? @post.root.tags : @post.tags
return tags.map(&:name) if tags
[]
end
def comma_separated_tags
tags.join(", ")
end
def url
post_url @post
end
def description
message.plain_text_without_markdown(truncate: 1000)
end
end end
class TagStreamPresenter < BasePresenter
def title
@presentable.display_tag_name
end
def metas_attributes
{
keywords: {name: "keywords", content: tag_name},
description: {name: "description", content: description},
og_url: {property: "og:url", content: url},
og_title: {property: "og:title", content: title},
og_description: {property: "og:description", content: description}
}
end
private
def description
I18n.t("streams.tags.title", tags: tag_name)
end
def url
tag_url tag_name
end
end
- if @post.present?
%link{:rel => 'alternate', :type => "application/json+oembed", :href => "#{oembed_url(:url => post_url(@post))}"}
= og_page_post_tags(@post)
- else
= og_general_tags
...@@ -4,20 +4,17 @@ ...@@ -4,20 +4,17 @@
!!! !!!
%html{lang: I18n.locale.to_s, dir: (rtl?) ? 'rtl' : 'ltr'} %html{lang: I18n.locale.to_s, dir: (rtl?) ? 'rtl' : 'ltr'}
%head{prefix: og_prefix} %head{prefix: og_prefix}
%title %title
= page_title yield(:page_title) = page_title yield(:page_title)
%meta{charset: 'utf-8'}/ %meta{charset: 'utf-8'}/
%meta{"http-equiv" => "Content-Type", :content=>"text/html; charset=utf-8"}/ %meta{"http-equiv" => "Content-Type", :content=>"text/html; charset=utf-8"}/
%meta{name: "description", content: "diaspora*"}/
%meta{name: "author", content: "diaspora* contributors"}/
%meta{name: "viewport", content: "width=device-width, initial-scale=1"}/ %meta{name: "viewport", content: "width=device-width, initial-scale=1"}/
= content_for?(:meta_data) ? yield(:meta_data) : metas_tags
%link{rel: 'shortcut icon', href: "#{image_path('favicon.png')}" } %link{rel: 'shortcut icon', href: "#{image_path('favicon.png')}" }
= render 'layouts/open_graph'
= chartbeat_head_block = chartbeat_head_block
= include_mixpanel = include_mixpanel
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
!!! !!!
%html{:lang => I18n.locale.to_s, :dir => (rtl?) ? 'rtl' : 'ltr'} %html{:lang => I18n.locale.to_s, :dir => (rtl?) ? 'rtl' : 'ltr'}
%head{:prefix => og_prefix} %head
%title %title
= pod_name = pod_name
...@@ -30,8 +30,7 @@ ...@@ -30,8 +30,7 @@
/ NOTE(we will enable these once we don't have to rely on back/forward buttons anymore) / NOTE(we will enable these once we don't have to rely on back/forward buttons anymore)
/%meta{:name => "apple-mobile-web-app-capable", :content => "yes"} /%meta{:name => "apple-mobile-web-app-capable", :content => "yes"}
/%link{:rel => "apple-touch-startup-image", :href => "/images/apple-splash.png"} /%link{:rel => "apple-touch-startup-image", :href => "/images/apple-splash.png"}
= yield :meta_data
= render 'layouts/open_graph'
= chartbeat_head_block = chartbeat_head_block
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
-# the COPYRIGHT file. -# the COPYRIGHT file.
- content_for :page_title do - content_for :page_title do
= @person.name = @presenter.name
- content_for :meta_data do
= metas_tags @presenter.metas_attributes
.container-fluid#profile_container .container-fluid#profile_container
.row .row
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
-# the COPYRIGHT file. -# the COPYRIGHT file.
- content_for :page_title do - content_for :page_title do
= post_page_title post = post.page_title
- content_for :meta_data do
= metas_tags post.metas_attributes
- content_for :content do - content_for :content do
#container.container-fluid #container.container-fluid
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
- content_for :page_title do - content_for :page_title do
= @stream.display_tag_name = @stream.display_tag_name
- content_for :meta_data do
= metas_tags @stream.metas_attributes
.container-fluid#tags_show .container-fluid#tags_show
.row .row
.col-md-3.hidden-xs .col-md-3.hidden-xs
......
...@@ -144,6 +144,9 @@ defaults: ...@@ -144,6 +144,9 @@ defaults:
limit_removals_to_per_day: 100 limit_removals_to_per_day: 100
source_url: source_url:
default_color_theme: "original" default_color_theme: "original"
default_metas:
title: 'diaspora* social network'
description: 'diaspora* is the online social world where you are in control.'
services: services:
facebook: facebook:
enable: false enable: false
......
...@@ -539,6 +539,15 @@ configuration: ## Section ...@@ -539,6 +539,15 @@ configuration: ## Section
## ("original" for the theme in "app/assets/stylesheets/color_themes/original/", for ## ("original" for the theme in "app/assets/stylesheets/color_themes/original/", for
## example). ## example).
#default_color_theme: "original" #default_color_theme: "original"
## Default meta tags
## You can change here the default meta tags content included on the pages of your pod.
## Title will be used for the opengraph og:site_name property while description will be used
## for description and og:description.
default_metas:
#title: 'diaspora* social network'
#description: 'diaspora* is the online social world where you are in control.'
## Posting from Diaspora to external services (all are disabled by default). ## Posting from Diaspora to external services (all are disabled by default).
services: ## Section services: ## Section
......
...@@ -146,6 +146,11 @@ describe PeopleController, :type => :controller do ...@@ -146,6 +146,11 @@ describe PeopleController, :type => :controller do
end end
describe '#show' do describe '#show' do
before do
@person = FactoryGirl.create(:user).person
@presenter = PersonPresenter.new(@person, @user)
end
it "404s if the id is invalid" do it "404s if the id is invalid" do
get :show, :id => 'delicious' get :show, :id => 'delicious'
expect(response.code).to eq("404") expect(response.code).to eq("404")
...@@ -161,9 +166,15 @@ describe PeopleController, :type => :controller do ...@@ -161,9 +166,15 @@ describe PeopleController, :type => :controller do
expect(response.code).to eq("404") expect(response.code).to eq("404")
end end
it "returns a person presenter" do
expect(PersonPresenter).to receive(:new).with(@person, @user).and_return(@presenter)
get :show, username: @person.username
expect(assigns(:presenter).to_json).to eq(@presenter.to_json)
end
it 'finds a person via username' do it 'finds a person via username' do
get :show, username: @user.username get :show, username: @person.username
expect(assigns(:person)).to eq(@user.person) expect(assigns(:presenter).to_json).to eq(@presenter.to_json)
end end
it "404s if no person is found via diaspora handle" do it "404s if no person is found via diaspora handle" do
...@@ -172,8 +183,8 @@ describe PeopleController, :type => :controller do ...@@ -172,8 +183,8 @@ describe PeopleController, :type => :controller do
end end
it 'finds a person via diaspora handle' do it 'finds a person via diaspora handle' do
get :show, username: @user.diaspora_handle get :show, username: @person.diaspora_handle
expect(assigns(:person)).to eq(@user.person) expect(assigns(:presenter).to_json).to eq(@presenter.to_json)
end end
it 'redirects home for closed account' do it 'redirects home for closed account' do
...@@ -216,8 +227,8 @@ describe PeopleController, :type => :controller do ...@@ -216,8 +227,8 @@ describe PeopleController, :type => :controller do
end end
it "assigns the right person" do it "assigns the right person" do
get :show, :id => @user.person.to_param get :show, id: @person.to_param
expect(assigns(:person)).to eq(@user.person) expect(assigns(:presenter).id).to eq(@presenter.id)
end end
end end
...@@ -249,6 +260,27 @@ describe PeopleController, :type => :controller do ...@@ -249,6 +260,27 @@ describe PeopleController, :type => :controller do
get :show, id: @person.to_param get :show, id: @person.to_param
expect(response.body).not_to include(@person.profile.bio) expect(response.body).not_to include(@person.profile.bio)
end end
it "includes the correct meta tags" do
presenter = PersonPresenter.new(@person)
methods_properties = {
comma_separated_tags: {html_attribute: "name", name: "keywords"},
url: {html_attribute: "property", name: "og:url"},
title: {html_attribute: "property", name: "og:title"},
image_url: {html_attribute: "property", name: "og:image"},
first_name: {html_attribute: "property", name: "og:profile:first_name"},
last_name: {html_attribute: "property", name: "og:profile:last_name"}
}
get :show, id: @person.to_param
methods_properties.each do |method, property|
value = presenter.send(method)
expect(response.body).to include(
"<meta #{property[:html_attribute]}=\"#{property[:name]}\" content=\"#{value}\" />"
)
end
end
end end
context "when the person is a contact of the current user" do context "when the person is a contact of the current user" do
......
...@@ -64,6 +64,7 @@ describe PostsController, type: :controller do ...@@ -64,6 +64,7 @@ describe PostsController, type: :controller do
context "user not signed in" do context "user not signed in" do
context "given a public post" do context "given a public post" do
let(:public) { alice.post(:status_message, text: "hello", public: true) } let(:public) { alice.post(:status_message, text: "hello", public: true) }
let(:public_with_tags) { alice.post(:status_message, text: "#hi #howareyou", public: true) }
it "shows a public post" do it "shows a public post" do
get :show, id: public.id get :show, id: public.id
...@@ -81,6 +82,35 @@ describe PostsController, type: :controller do ...@@ -81,6 +82,35 @@ describe PostsController, type: :controller do
expected_xml = DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(public)).to_xml expected_xml = DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(public)).to_xml
expect(response.body).to eq(expected_xml) expect(response.body).to eq(expected_xml)
end end
it "includes the correct uniques meta tags" do
presenter = PostPresenter.new(public)
methods_properties = {
comma_separated_tags: {html_attribute: "name", name: "keywords"},
description: {html_attribute: "name", name: "description"},
url: {html_attribute: "property", name: "og:url"},
title: {html_attribute: "property", name: "og:title"},
published_time_iso8601: {html_attribute: "property", name: "og:article:published_time"},
modified_time_iso8601: {html_attribute: "property", name: "og:article:modified_time"},
author_name: {html_attribute: "property", name: "og:article:author"}
}
get :show, id: public.id, format: :html
methods_properties.each do |method, property|
value = presenter.send(method)
expect(response.body).to include(
"<meta #{property[:html_attribute]}=\"#{property[:name]}\" content=\"#{value}\" />"
)
end
end
it "includes the correct multiple meta tags" do
get :show, id: public_with_tags.id, format: :html
expect(response.body).to include('<meta property="og:article:tag" content="hi" />')
expect(response.body).to include('<meta property="og:article:tag" content="howareyou" />')
end
end end
context "given a limited post" do context "given a limited post" do
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter