Merge pull request #184 from jekyll/drop
Convert seo_author_* logic to a drop
This commit is contained in:
commit
6a373369e8
|
@ -1,7 +1,12 @@
|
|||
require "jekyll"
|
||||
require "jekyll-seo-tag/version"
|
||||
|
||||
module Jekyll
|
||||
class SeoTag < Liquid::Tag
|
||||
autoload :JSONLD, "jekyll-seo-tag/json_ld"
|
||||
autoload :Drop, "jekyll-seo-tag/drop"
|
||||
autoload :Filters, "jekyll-seo-tag/filters"
|
||||
|
||||
attr_accessor :context
|
||||
|
||||
# Matches all whitespace that follows either
|
||||
|
@ -39,12 +44,12 @@ module Jekyll
|
|||
"page" => context.registers[:page],
|
||||
"site" => context.registers[:site].site_payload["site"],
|
||||
"paginator" => context["paginator"],
|
||||
"seo_tag" => options,
|
||||
"seo_tag" => drop,
|
||||
}
|
||||
end
|
||||
|
||||
def title?
|
||||
@text !~ %r!title=false!i
|
||||
def drop
|
||||
@drop ||= Jekyll::SeoTag::Drop.new(@text, @context)
|
||||
end
|
||||
|
||||
def info
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
module Jekyll
|
||||
class SeoTag
|
||||
class Drop < Jekyll::Drops::Drop
|
||||
include Jekyll::SeoTag::JSONLD
|
||||
|
||||
TITLE_SEPARATOR = " | ".freeze
|
||||
FORMAT_STRING_METHODS = %i[
|
||||
markdownify strip_html normalize_whitespace escape_once
|
||||
].freeze
|
||||
HOMEPAGE_OR_ABOUT_REGEX = %r!^/(about/)?(index.html?)?$!
|
||||
|
||||
def initialize(text, context)
|
||||
@obj = {}
|
||||
@mutations = {}
|
||||
@text = text
|
||||
@context = context
|
||||
end
|
||||
|
||||
def version
|
||||
Jekyll::SeoTag::VERSION
|
||||
end
|
||||
|
||||
# Should the `<title>` tag be generated for this page?
|
||||
def title?
|
||||
return false unless title
|
||||
return @display_title if defined?(@display_title)
|
||||
@display_title = (@text !~ %r!title=false!i)
|
||||
end
|
||||
|
||||
def site_title
|
||||
@site_title ||= format_string(site["title"] || site["name"])
|
||||
end
|
||||
|
||||
# Page title without site title or description appended
|
||||
def page_title
|
||||
@page_title ||= format_string(page["title"] || site_title)
|
||||
end
|
||||
|
||||
# Page title with site title or description appended
|
||||
def title
|
||||
@title ||= begin
|
||||
if page["title"] && site_title
|
||||
page_title + TITLE_SEPARATOR + site_title
|
||||
elsif site["description"] && site_title
|
||||
site_title + TITLE_SEPARATOR + format_string(site["description"])
|
||||
else
|
||||
page_title || site_title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
return @name if defined?(@name)
|
||||
@name = if seo_name
|
||||
seo_name
|
||||
elsif !homepage_or_about?
|
||||
nil
|
||||
elsif site["social"] && site["social"]["name"]
|
||||
format_string site["social"]["name"]
|
||||
elsif site_title
|
||||
format_string site_title
|
||||
end
|
||||
end
|
||||
|
||||
def description
|
||||
@description ||= format_string(
|
||||
page["description"] || page["excerpt"] || site["description"]
|
||||
)
|
||||
end
|
||||
|
||||
# Returns a nil or a hash representing the author
|
||||
# Author name will be pulled from:
|
||||
#
|
||||
# 1. The `author` key, if the key is a string
|
||||
# 2. The first author in the `authors` key
|
||||
# 3. The `author` key in the site config
|
||||
#
|
||||
# If the result from the name search is a string, we'll also check
|
||||
# to see if the author exists in `site.data.authors`
|
||||
def author
|
||||
@author ||= begin
|
||||
return if author_string_or_hash.to_s.empty?
|
||||
|
||||
author = if author_string_or_hash.is_a?(String)
|
||||
author_hash(author_string_or_hash)
|
||||
else
|
||||
author_string_or_hash
|
||||
end
|
||||
|
||||
author["twitter"] ||= author["name"]
|
||||
author["twitter"].delete! "@"
|
||||
author.to_liquid
|
||||
end
|
||||
end
|
||||
|
||||
def date_modified
|
||||
@date_modified ||= begin
|
||||
date = if page["seo"] && page["seo"]["date_modified"]
|
||||
page["seo"]["date_modified"]
|
||||
else
|
||||
page["last_modified_at"] || page["date"]
|
||||
end
|
||||
filters.date_to_xmlschema(date) if date
|
||||
end
|
||||
end
|
||||
|
||||
def date_published
|
||||
@date_published ||= filters.date_to_xmlschema(page["date"]) if page["date"]
|
||||
end
|
||||
|
||||
def type
|
||||
@type ||= begin
|
||||
if page["seo"] && page["seo"]["type"]
|
||||
page["seo"]["type"]
|
||||
elsif homepage_or_about?
|
||||
"WebSite"
|
||||
elsif page["date"]
|
||||
"BlogPosting"
|
||||
else
|
||||
"WebPage"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def links
|
||||
@links ||= begin
|
||||
if page["seo"] && page["seo"]["links"]
|
||||
page["seo"]["links"]
|
||||
elsif homepage_or_about? && site["social"] && site["social"]["links"]
|
||||
site["social"]["links"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def logo
|
||||
@logo ||= begin
|
||||
return unless site["logo"]
|
||||
if absolute_url? site["logo"]
|
||||
filters.uri_escape site["logo"]
|
||||
else
|
||||
filters.uri_escape filters.absolute_url site["logo"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns nil or a hash representing the page image
|
||||
# The image hash will always contain a path, pulled from:
|
||||
#
|
||||
# 1. The `image` key if it's a string
|
||||
# 2. The `image.path` key if it's a hash
|
||||
# 3. The `image.facebook` key
|
||||
# 4. The `image.twitter` key
|
||||
#
|
||||
# The resulting path is always an absolute URL
|
||||
def image
|
||||
return @image if defined?(@image)
|
||||
|
||||
image = page["image"]
|
||||
return @image = nil unless image
|
||||
|
||||
image = { "path" => image } if image.is_a?(String)
|
||||
image["path"] ||= image["facebook"] || image["twitter"]
|
||||
|
||||
unless absolute_url? image["path"]
|
||||
image["path"] = filters.absolute_url image["path"]
|
||||
end
|
||||
|
||||
image["path"] = filters.uri_escape image["path"]
|
||||
|
||||
@image = image.to_liquid
|
||||
end
|
||||
|
||||
def page_lang
|
||||
@page_lang ||= page["lang"] || site["lang"] || "en_US"
|
||||
end
|
||||
|
||||
def canonical_url
|
||||
@canonical_url ||= filters.absolute_url(page["url"]).gsub(%r!/index\.html$!, "/")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filters
|
||||
@filters ||= Jekyll::SeoTag::Filters.new(@context)
|
||||
end
|
||||
|
||||
def page
|
||||
@page ||= @context.registers[:page].to_liquid
|
||||
end
|
||||
|
||||
def site
|
||||
@site ||= @context.registers[:site].site_payload["site"].to_liquid
|
||||
end
|
||||
|
||||
def homepage_or_about?
|
||||
page["url"] =~ HOMEPAGE_OR_ABOUT_REGEX
|
||||
end
|
||||
|
||||
attr_reader :context
|
||||
|
||||
def fallback_data
|
||||
@fallback_data ||= {}
|
||||
end
|
||||
|
||||
def absolute_url?(string)
|
||||
Addressable::URI.parse(string).absolute?
|
||||
end
|
||||
|
||||
def format_string(string)
|
||||
string = FORMAT_STRING_METHODS.reduce(string) do |memo, method|
|
||||
filters.public_send(method, memo)
|
||||
end
|
||||
|
||||
string unless string.empty?
|
||||
end
|
||||
|
||||
def author_string_or_hash
|
||||
@author_string_or_hash ||= begin
|
||||
author = page["author"]
|
||||
author = page["authors"][0] if author.to_s.empty? && page["authors"]
|
||||
author = site["author"] if author.to_s.empty?
|
||||
author
|
||||
end
|
||||
end
|
||||
|
||||
def author_hash(author_string)
|
||||
if site.data["authors"] && site.data["authors"][author_string]
|
||||
hash = site.data["authors"][author_string]
|
||||
hash["twitter"] ||= author_string
|
||||
hash
|
||||
else
|
||||
{ "name" => author_string }
|
||||
end
|
||||
end
|
||||
|
||||
def seo_name
|
||||
@seo_name ||= format_string(page["seo"]["name"]) if page["seo"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
module Jekyll
|
||||
class SeoTag
|
||||
class Filters
|
||||
include Jekyll::Filters
|
||||
include Liquid::StandardFilters
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,79 @@
|
|||
module Jekyll
|
||||
class SeoTag
|
||||
module JSONLD
|
||||
|
||||
# A hash of instance methods => key in resulting JSON-LD hash
|
||||
METHODS_KEYS = {
|
||||
:json_context => "@context",
|
||||
:type => "@type",
|
||||
:name => "name",
|
||||
:page_title => "headline",
|
||||
:json_author => "author",
|
||||
:json_image => "image",
|
||||
:date_published => "datePublished",
|
||||
:date_modified => "dateModified",
|
||||
:description => "description",
|
||||
:publisher => "publisher",
|
||||
:main_entity => "mainEntityOfPage",
|
||||
:links => "sameAs",
|
||||
:canonical_url => "url",
|
||||
}.freeze
|
||||
|
||||
def json_ld
|
||||
@json_ld ||= begin
|
||||
output = {}
|
||||
METHODS_KEYS.each do |method, key|
|
||||
value = send(method)
|
||||
output[key] = value unless value.nil?
|
||||
end
|
||||
output
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def json_context
|
||||
"http://schema.org"
|
||||
end
|
||||
|
||||
def json_author
|
||||
return unless author
|
||||
{
|
||||
"@type" => "Person",
|
||||
"name" => author["name"],
|
||||
}
|
||||
end
|
||||
|
||||
def json_image
|
||||
return unless image
|
||||
return image["path"] if image.length == 1
|
||||
|
||||
hash = image.dup
|
||||
hash["url"] = hash.delete("path")
|
||||
hash["@type"] = "imageObject"
|
||||
hash
|
||||
end
|
||||
|
||||
def publisher
|
||||
return unless logo
|
||||
output = {
|
||||
"@type" => "Organization",
|
||||
"logo" => {
|
||||
"@type" => "ImageObject",
|
||||
"url" => logo,
|
||||
},
|
||||
}
|
||||
output["name"] = author["name"] if author
|
||||
output
|
||||
end
|
||||
|
||||
def main_entity
|
||||
return unless %w(BlogPosting CreativeWork).include?(type)
|
||||
{
|
||||
"@type" => "WebPage",
|
||||
"@id" => canonical_url,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,143 +1,39 @@
|
|||
<!-- Begin Jekyll SEO tag v{{ seo_tag.version }} -->
|
||||
|
||||
{% if page.url == "/" or page.url == "/about/" %}
|
||||
{% assign seo_homepage_or_about = true %}
|
||||
{% if seo_tag.title? %}
|
||||
<title>{{ seo_tag.title }}</title>
|
||||
{% endif %}
|
||||
|
||||
{% assign seo_site_title = site.title | default: site.name %}
|
||||
{% assign seo_page_title = page.title | default: seo_site_title %}
|
||||
{% assign seo_title = page.title | default: seo_site_title %}
|
||||
|
||||
{% if page.title and seo_site_title %}
|
||||
{% assign seo_title = page.title | append:" | " | append: seo_site_title %}
|
||||
{% elsif site.description and seo_site_title %}
|
||||
{% assign seo_title = seo_site_title | append:" | " | append: site.description %}
|
||||
{% if seo_tag.page_title %}
|
||||
<meta property="og:title" content="{{ seo_tag.page_title }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if page.seo and page.seo.name %}
|
||||
{% assign seo_name = page.seo.name %}
|
||||
{% elsif seo_homepage_or_about and site.social and site.social.name %}
|
||||
{% assign seo_name = site.social.name %}
|
||||
{% elsif seo_homepage_or_about and seo_site_title %}
|
||||
{% assign seo_name = seo_site_title %}
|
||||
{% endif %}
|
||||
{% if seo_name %}
|
||||
{% assign seo_name = seo_name | smartify | strip_html | normalize_whitespace | escape_once %}
|
||||
{% if seo_tag.author.name %}
|
||||
<meta name="author" content="{{ seo_tag.author.name }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_title %}
|
||||
{% assign seo_title = seo_title | smartify | strip_html | normalize_whitespace | escape_once %}
|
||||
<meta property="og:locale" content="{{ seo_tag.page_lang | replace:'-','_' }}" />
|
||||
|
||||
{% if seo_tag.description %}
|
||||
<meta name="description" content="{{ seo_tag.description }}" />
|
||||
<meta property="og:description" content="{{ seo_tag.description }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_site_title %}
|
||||
{% assign seo_site_title = seo_site_title | smartify | strip_html | normalize_whitespace | escape_once %}
|
||||
{% if site.url %}
|
||||
<link rel="canonical" href="{{ seo_tag.canonical_url }}" />
|
||||
<meta property="og:url" content="{{ seo_tag.canonical_url }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_page_title %}
|
||||
{% assign seo_page_title = seo_page_title | smartify | strip_html | normalize_whitespace | escape_once %}
|
||||
{% if seo_tag.site_title %}
|
||||
<meta property="og:site_name" content="{{ seo_tag.site_title }}" />
|
||||
{% endif %}
|
||||
|
||||
{% assign seo_description = page.description | default: page.excerpt | default: site.description %}
|
||||
{% if seo_description %}
|
||||
{% assign seo_description = seo_description | markdownify | strip_html | normalize_whitespace | escape_once %}
|
||||
{% endif %}
|
||||
|
||||
{% assign seo_author = page.author | default: page.authors[0] | default: site.author %}
|
||||
{% if seo_author %}
|
||||
{% if seo_author.name %}
|
||||
{% assign seo_author_name = seo_author.name %}
|
||||
{% else %}
|
||||
{% if site.data.authors and site.data.authors[seo_author] %}
|
||||
{% assign seo_author_name = site.data.authors[seo_author].name %}
|
||||
{% else %}
|
||||
{% assign seo_author_name = seo_author %}
|
||||
{% endif %}
|
||||
{% if seo_tag.image %}
|
||||
<meta property="og:image" content="{{ seo_tag.image.path }}" />
|
||||
{% if seo_tag.image.height %}
|
||||
<meta property="og:image:height" content="{{ seo_tag.image.height }}" />
|
||||
{% endif %}
|
||||
{% if seo_author.twitter %}
|
||||
{% assign seo_author_twitter = seo_author.twitter %}
|
||||
{% else %}
|
||||
{% if site.data.authors and site.data.authors[seo_author] %}
|
||||
{% assign seo_author_twitter = site.data.authors[seo_author].twitter %}
|
||||
{% else %}
|
||||
{% assign seo_author_twitter = seo_author %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% assign seo_author_twitter = seo_author_twitter | replace:"@","" %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.date_modified or page.last_modified_at or page.date %}
|
||||
{% assign seo_date_modified = page.seo.date_modified | default: page.last_modified_at %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.seo and page.seo.type %}
|
||||
{% assign seo_type = page.seo.type %}
|
||||
{% elsif seo_homepage_or_about %}
|
||||
{% assign seo_type = "WebSite" %}
|
||||
{% elsif page.date %}
|
||||
{% assign seo_type = "BlogPosting" %}
|
||||
{% else %}
|
||||
{% assign seo_type = "WebPage" %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.seo and page.seo.links %}
|
||||
{% assign seo_links = page.seo.links %}
|
||||
{% elsif seo_homepage_or_about and site.social and site.social.links %}
|
||||
{% assign seo_links = site.social.links %}
|
||||
{% endif %}
|
||||
|
||||
{% if site.logo %}
|
||||
{% assign seo_site_logo = site.logo %}
|
||||
{% unless seo_site_logo contains "://" %}
|
||||
{% assign seo_site_logo = seo_site_logo | absolute_url %}
|
||||
{% endunless %}
|
||||
{% assign seo_site_logo = seo_site_logo | escape %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.image %}
|
||||
{% assign seo_page_image = page.image.path | default: page.image.facebook | default: page.image.twitter | default: page.image %}
|
||||
{% unless seo_page_image contains "://" %}
|
||||
{% assign seo_page_image = seo_page_image | absolute_url %}
|
||||
{% endunless %}
|
||||
{% assign seo_page_image = seo_page_image | escape %}
|
||||
{% endif %}
|
||||
|
||||
{% assign seo_page_lang = page.lang | default: site.lang | default: "en_US" %}
|
||||
|
||||
{% if seo_tag.title and seo_title %}
|
||||
<title>{{ seo_title }}</title>
|
||||
{% endif %}
|
||||
|
||||
{% if seo_page_title %}
|
||||
<meta property="og:title" content="{{ seo_page_title }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_author_name %}
|
||||
<meta name="author" content="{{ seo_author_name }}" />
|
||||
{% endif %}
|
||||
|
||||
<meta property="og:locale" content="{{ seo_page_lang | replace:'-','_' }}" />
|
||||
|
||||
{% if seo_description %}
|
||||
<meta name="description" content="{{ seo_description }}" />
|
||||
<meta property="og:description" content="{{ seo_description }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if page.url %}
|
||||
<link rel="canonical" href="{{ page.url | replace:'/index.html','/' | absolute_url }}" />
|
||||
<meta property="og:url" content="{{ page.url | replace:'/index.html','/' | absolute_url }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_site_title %}
|
||||
<meta property="og:site_name" content="{{ seo_site_title }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if seo_page_image %}
|
||||
<meta property="og:image" content="{{ seo_page_image }}" />
|
||||
{% if page.image.height %}
|
||||
<meta property="og:image:height" content="{{ page.image.height }}" />
|
||||
{% endif %}
|
||||
{% if page.image.width %}
|
||||
<meta property="og:image:width" content="{{ page.image.width }}" />
|
||||
{% if seo_tag.image.width %}
|
||||
<meta property="og:image:width" content="{{ seo_tag.image.width }}" />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -154,7 +50,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if site.twitter %}
|
||||
{% if seo_page_image or page.image.twitter %}
|
||||
{% if seo_tag.image %}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
{% else %}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
|
@ -162,8 +58,8 @@
|
|||
|
||||
<meta name="twitter:site" content="@{{ site.twitter.username | replace:"@","" }}" />
|
||||
|
||||
{% if seo_author_twitter %}
|
||||
<meta name="twitter:creator" content="@{{ seo_author_twitter }}" />
|
||||
{% if seo_tag.author.twitter %}
|
||||
<meta name="twitter:creator" content="@{{ seo_tag.author.twitter }}" />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -185,12 +81,15 @@
|
|||
{% if site.webmaster_verifications.google %}
|
||||
<meta name="google-site-verification" content="{{ site.webmaster_verifications.google }}">
|
||||
{% endif %}
|
||||
|
||||
{% if site.webmaster_verifications.bing %}
|
||||
<meta name="msvalidate.01" content="{{ site.webmaster_verifications.bing }}">
|
||||
{% endif %}
|
||||
|
||||
{% if site.webmaster_verifications.alexa %}
|
||||
<meta name="alexaVerifyID" content="{{ site.webmaster_verifications.alexa }}">
|
||||
{% endif %}
|
||||
|
||||
{% if site.webmaster_verifications.yandex %}
|
||||
<meta name="yandex-verification" content="{{ site.webmaster_verifications.yandex }}">
|
||||
{% endif %}
|
||||
|
@ -198,81 +97,8 @@
|
|||
<meta name="google-site-verification" content="{{ site.google_site_verification }}" />
|
||||
{% endif %}
|
||||
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
|
||||
{% if seo_type %}
|
||||
"@type": {{ seo_type | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_name %}
|
||||
"name": {{ seo_name | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_page_title %}
|
||||
"headline": {{ seo_page_title | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_author %}
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": {{ seo_author | jsonify }}
|
||||
},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_page_image %}
|
||||
{% if page.image.height && page.image.width %}
|
||||
"image": {
|
||||
"@type": "ImageObject",
|
||||
"url": {{ seo_page_image | jsonify }},
|
||||
"height": {{ page.image.height | jsonify }},
|
||||
"width": {{ page.image.width | jsonify }}
|
||||
},
|
||||
{% else %}
|
||||
"image": {{ seo_page_image | jsonify }},
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.date %}
|
||||
"datePublished": {{ page.date | date_to_xmlschema | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_date_modified %}
|
||||
"dateModified": {{ seo_date_modified | date_to_xmlschema | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_description %}
|
||||
"description": {{ seo_description | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_site_logo %}
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
{% if seo_author %}
|
||||
"name": {{ seo_author | jsonify }},
|
||||
{% endif %}
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": {{ seo_site_logo | jsonify }}
|
||||
}
|
||||
},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_type == "BlogPosting" or seo_type == "CreativeWork"%}
|
||||
"mainEntityOfPage": {
|
||||
"@type": "WebPage",
|
||||
"@id": {{ page.url | replace:'/index.html','/' | absolute_url | jsonify }}
|
||||
},
|
||||
{% endif %}
|
||||
|
||||
{% if seo_links %}
|
||||
"sameAs": {{ seo_links | jsonify }},
|
||||
{% endif %}
|
||||
|
||||
"url": {{ page.url | replace:'/index.html','/' | absolute_url | jsonify }}
|
||||
}
|
||||
{{ seo_tag.json_ld | jsonify }}
|
||||
</script>
|
||||
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
RSpec.describe Jekyll::SeoTag::Drop do
|
||||
let(:config) { { "title" => "site title" } }
|
||||
let(:page_meta) { { "title" => "page title" } }
|
||||
let(:page) { make_page(page_meta) }
|
||||
let(:site) { make_site(config) }
|
||||
let(:context) { make_context(:page => page, :site => site) }
|
||||
let(:text) { "" }
|
||||
subject { described_class.new(text, context) }
|
||||
|
||||
before do
|
||||
Jekyll.logger.log_level = :error
|
||||
end
|
||||
|
||||
# Drop includes liquid filters which expect arguments
|
||||
# By default, in drops, `to_h` will call each public method with no arugments
|
||||
# Here, that would cause the filters to explode. This test ensures that all
|
||||
# public methods don't explode when called without arguments. Don't explode.
|
||||
it "doesn't blow up on to_h" do
|
||||
expect { subject.to_h }.to_not raise_error
|
||||
end
|
||||
|
||||
it "returns the version" do
|
||||
expect(subject.version).to eql(Jekyll::SeoTag::VERSION)
|
||||
end
|
||||
|
||||
context "title?" do
|
||||
it "knows to include the title" do
|
||||
expect(subject.title?).to be_truthy
|
||||
end
|
||||
|
||||
context "with title=false" do
|
||||
let(:text) { "title=false" }
|
||||
|
||||
it "knows not to include the title" do
|
||||
expect(subject.title?).to be_falsy
|
||||
end
|
||||
end
|
||||
|
||||
context "site title" do
|
||||
it "knows the site title" do
|
||||
expect(subject.site_title).to eql("site title")
|
||||
end
|
||||
|
||||
context "with site.name" do
|
||||
let(:config) { { "name" => "site title" } }
|
||||
|
||||
it "knows the site title" do
|
||||
expect(subject.site_title).to eql("site title")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "page title" do
|
||||
it "knows the page title" do
|
||||
expect(subject.page_title).to eql("page title")
|
||||
end
|
||||
|
||||
context "without a page title" do
|
||||
let(:page) { make_page }
|
||||
|
||||
it "knows the page title" do
|
||||
expect(subject.page_title).to eql("site title")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "title" do
|
||||
context "with a page and site title" do
|
||||
it "builds the title" do
|
||||
expect(subject.title).to eql("page title | site title")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a site description but no page title" do
|
||||
let(:page) { make_page }
|
||||
let(:config) do
|
||||
{ "title" => "site title", "description" => "site description" }
|
||||
end
|
||||
|
||||
it "builds the title" do
|
||||
expect(subject.title).to eql("site title | site description")
|
||||
end
|
||||
end
|
||||
|
||||
context "with just a page title" do
|
||||
let(:site) { make_site }
|
||||
|
||||
it "builds the title" do
|
||||
expect(subject.title).to eql("page title")
|
||||
end
|
||||
end
|
||||
|
||||
context "with just a site title" do
|
||||
let(:page) { make_page }
|
||||
|
||||
it "builds the title" do
|
||||
expect(subject.title).to eql("site title")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "name" do
|
||||
context "with seo.name" do
|
||||
let(:page_meta) do
|
||||
{ "seo" => { "name" => "seo name" } }
|
||||
end
|
||||
|
||||
it "uses the seo name" do
|
||||
expect(subject.name).to eql("seo name")
|
||||
end
|
||||
end
|
||||
|
||||
context "the index" do
|
||||
let(:page_meta) { { "permalink" => "/" } }
|
||||
|
||||
context "with site.social.name" do
|
||||
let(:config) { { "social" => { "name" => "social name" } } }
|
||||
|
||||
it "uses site.social.name" do
|
||||
expect(subject.name).to eql("social name")
|
||||
end
|
||||
end
|
||||
|
||||
it "uses the site title" do
|
||||
expect(subject.name).to eql("site title")
|
||||
end
|
||||
end
|
||||
|
||||
context "description" do
|
||||
context "with a page description" do
|
||||
let(:page_meta) { { "description"=> "page description" } }
|
||||
|
||||
it "uses the page description" do
|
||||
expect(subject.description).to eql("page description")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a page excerpt" do
|
||||
let(:page_meta) { { "description"=> "page excerpt" } }
|
||||
|
||||
it "uses the page description" do
|
||||
expect(subject.description).to eql("page excerpt")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a site description" do
|
||||
let(:config) { { "description"=> "site description" } }
|
||||
|
||||
it "uses the page description" do
|
||||
expect(subject.description).to eql("site description")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "author" do
|
||||
let(:data) { {} }
|
||||
let(:config) { { "author" => "author" } }
|
||||
let(:site) do
|
||||
site = make_site(config)
|
||||
site.data = data
|
||||
site
|
||||
end
|
||||
|
||||
%i[with without].each do |site_data_type|
|
||||
context "#{site_data_type} site.author data" do
|
||||
let(:data) do
|
||||
if site_data_type == :with
|
||||
{
|
||||
"authors" => {
|
||||
"author" => { "name" => "Author" },
|
||||
},
|
||||
}
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
:string => { "author" => "author" },
|
||||
:array => { "authors" => %w(author author2) },
|
||||
:empty_string => { "author" => "" },
|
||||
:nil => { "author" => nil },
|
||||
:hash => { "author" => { "name" => "author" } },
|
||||
}.each do |author_type, data|
|
||||
context "with author as #{author_type}" do
|
||||
let(:page_meta) { data }
|
||||
|
||||
it "returns a hash" do
|
||||
expect(subject.author).to be_a(Hash)
|
||||
end
|
||||
|
||||
it "returns the name" do
|
||||
if site_data_type == :with && author_type != :hash
|
||||
expect(subject.author["name"]).to eql("Author")
|
||||
else
|
||||
expect(subject.author["name"]).to eql("author")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the twitter handle" do
|
||||
expect(subject.author["twitter"]).to eql("author")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "twitter" do
|
||||
let(:page_meta) { { "author" => "author" } }
|
||||
|
||||
it "pulls the handle from the author" do
|
||||
expect(subject.author["twitter"]).to eql("author")
|
||||
end
|
||||
|
||||
context "with an @" do
|
||||
let(:page_meta) do
|
||||
{
|
||||
"author" => {
|
||||
"name" => "author",
|
||||
"twitter" => "@twitter",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
it "strips the @" do
|
||||
expect(subject.author["twitter"]).to eql("twitter")
|
||||
end
|
||||
end
|
||||
|
||||
context "with an explicit handle" do
|
||||
let(:page_meta) do
|
||||
{
|
||||
"author" => {
|
||||
"name" => "author",
|
||||
"twitter" => "twitter",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
it "pulls the handle from the hash" do
|
||||
expect(subject.author["twitter"]).to eql("twitter")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "date published" do
|
||||
let(:config) { { "timezone" => "America/New_York" } }
|
||||
let(:page_meta) { { "date" => "2017-01-01" } }
|
||||
|
||||
it "uses page.date" do
|
||||
expect(subject.date_published).to eql("2017-01-01T00:00:00-05:00")
|
||||
end
|
||||
end
|
||||
|
||||
context "date modified" do
|
||||
let(:config) { { "timezone" => "America/New_York" } }
|
||||
|
||||
context "with seo.date_modified" do
|
||||
let(:page_meta) { { "seo" => { "date_modified" => "2017-01-01" } } }
|
||||
|
||||
it "uses seo.date_modified" do
|
||||
expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00")
|
||||
end
|
||||
end
|
||||
|
||||
context "with page.last_modified_at" do
|
||||
let(:page_meta) { { "last_modified_at" => "2017-01-01" } }
|
||||
|
||||
it "uses page.last_modified_at" do
|
||||
expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00")
|
||||
end
|
||||
end
|
||||
|
||||
context "date" do
|
||||
let(:page_meta) { { "date" => "2017-01-01" } }
|
||||
|
||||
it "uses page.date" do
|
||||
expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "type" do
|
||||
context "with seo.type set" do
|
||||
let(:page_meta) { { "seo" => { "type" => "test" } } }
|
||||
|
||||
it "uses seo.type" do
|
||||
expect(subject.type).to eql("test")
|
||||
end
|
||||
end
|
||||
|
||||
context "the homepage" do
|
||||
let(:page_meta) { { "permalink" => "/" } }
|
||||
|
||||
it "is a website" do
|
||||
expect(subject.type).to eql("WebSite")
|
||||
end
|
||||
end
|
||||
|
||||
context "the about page" do
|
||||
let(:page) { make_page({ "permalink" => "/about/" }) }
|
||||
|
||||
it "is a website" do
|
||||
expect(subject.type).to eql("WebSite")
|
||||
end
|
||||
end
|
||||
|
||||
context "a blog post" do
|
||||
let(:page_meta) { { "date" => "2017-01-01" } }
|
||||
|
||||
it "is a blog post" do
|
||||
expect(subject.type).to eql("BlogPosting")
|
||||
end
|
||||
end
|
||||
|
||||
it "is a webpage" do
|
||||
expect(subject.type).to eql("WebPage")
|
||||
end
|
||||
end
|
||||
|
||||
context "links" do
|
||||
context "with seo.links" do
|
||||
let(:page_meta) { { "seo" => { "links" => %w(foo bar) } } }
|
||||
|
||||
it "uses seo.links" do
|
||||
expect(subject.links).to eql(%w(foo bar))
|
||||
end
|
||||
end
|
||||
|
||||
context "with site.social.links" do
|
||||
let(:config) { { "social" => { "links"=> %w(a b) } } }
|
||||
|
||||
it "doesn't use site.social.links" do
|
||||
expect(subject.links).to be_nil
|
||||
end
|
||||
|
||||
context "the homepage" do
|
||||
let(:page_meta) { { "permalink" => "/" } }
|
||||
|
||||
it "uses site.social.links" do
|
||||
expect(subject.links).to eql(%w(a b))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "logo" do
|
||||
context "without site.logo" do
|
||||
it "returns nothing" do
|
||||
expect(subject.logo).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "with an absolute site.logo" do
|
||||
let(:config) { { "logo" => "http://example.com/image.png" } }
|
||||
|
||||
it "uses site.logo" do
|
||||
expect(subject.logo).to eql("http://example.com/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a relative site.logo" do
|
||||
let(:config) do
|
||||
{
|
||||
"logo" => "image.png",
|
||||
"url" => "http://example.com",
|
||||
}
|
||||
end
|
||||
|
||||
it "uses site.logo" do
|
||||
expect(subject.logo).to eql("http://example.com/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a uri-escaped logo" do
|
||||
let(:config) { { "logo" => "some image.png" } }
|
||||
|
||||
it "escapes the logo" do
|
||||
expect(subject.logo).to eql("/some%20image.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "image" do
|
||||
let(:page_meta) { { "image" => image } }
|
||||
|
||||
context "with image as a string" do
|
||||
let(:image) { "image.png" }
|
||||
|
||||
it "returns a hash" do
|
||||
expect(subject.image).to be_a(Hash)
|
||||
end
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject.image["path"]).to eql("/image.png")
|
||||
end
|
||||
|
||||
context "with site.url" do
|
||||
let(:config) { { "url" => "http://example.com" } }
|
||||
|
||||
it "makes the path absolute" do
|
||||
expect(subject.image["path"]).to eql("http://example.com/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a URL-escaped path" do
|
||||
let(:image) { "some image.png" }
|
||||
|
||||
it "URL-escapes the image" do
|
||||
expect(subject.image["path"]).to eql("/some%20image.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with image as a hash" do
|
||||
context "with a path" do
|
||||
let(:image) { { "path" => "image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject.image["path"]).to eql("/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with facebook" do
|
||||
let(:image) { { "facebook" => "image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject.image["path"]).to eql("/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with twitter" do
|
||||
let(:image) { { "twitter" => "image.png" } }
|
||||
|
||||
it "returns the image" do
|
||||
expect(subject.image["path"]).to eql("/image.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "with height and width" do
|
||||
let(:image) { { "path" => "image.png", "height" => 5, "width" => 10 } }
|
||||
|
||||
it "returns the height and width" do
|
||||
expect(subject.image["height"]).to eql(5)
|
||||
expect(subject.image["width"]).to eql(10)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "lang" do
|
||||
context "with page.lang" do
|
||||
let(:page_meta) { { "lang" => "en_GB" } }
|
||||
|
||||
it "uses page.lang" do
|
||||
expect(subject.page_lang).to eql("en_GB")
|
||||
end
|
||||
end
|
||||
|
||||
context "with site.lang" do
|
||||
let(:config) { { "lang" => "en_GB" } }
|
||||
|
||||
it "uses site.lang" do
|
||||
expect(subject.page_lang).to eql("en_GB")
|
||||
end
|
||||
end
|
||||
|
||||
context "with nothing" do
|
||||
it "defaults" do
|
||||
expect(subject.page_lang).to eql("en_US")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "homepage_or_about?" do
|
||||
[
|
||||
"/", "/index.html", "index.html", "/index.htm",
|
||||
"/about/", "/about/index.html",
|
||||
].each do |permalink|
|
||||
context "when passed '#{permalink}' as a permalink" do
|
||||
let(:page_meta) { { "permalink" => permalink } }
|
||||
|
||||
it "knows it's the home or about page" do
|
||||
expect(subject.send(:homepage_or_about?)).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "a random URL" do
|
||||
let(:page_meta) { { "permalink" => "/about-foo/" } }
|
||||
|
||||
it "knows it's not the home or about page" do
|
||||
expect(subject.send(:homepage_or_about?)).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
RSpec.describe Jekyll::SeoTag::Filters do
|
||||
let(:page) { make_page }
|
||||
let(:site) { make_site }
|
||||
let(:context) { make_context(:page => page, :site => site) }
|
||||
subject { described_class.new(context) }
|
||||
|
||||
before do
|
||||
Jekyll.logger.log_level = :error
|
||||
end
|
||||
|
||||
it "stores the context" do
|
||||
expect(subject.instance_variable_get("@context")).to be_a(Liquid::Context)
|
||||
end
|
||||
|
||||
it "exposes jekyll filters" do
|
||||
expect(subject).to respond_to(:markdownify)
|
||||
end
|
||||
|
||||
it "exposes liquid standard filters" do
|
||||
expect(subject).to respond_to(:normalize_whitespace)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,152 @@
|
|||
RSpec.describe Jekyll::SeoTag::JSONLD do
|
||||
before do
|
||||
Jekyll.logger.log_level = :error
|
||||
end
|
||||
|
||||
let(:author) { "author" }
|
||||
let(:image) { "image" }
|
||||
let(:metadata) do
|
||||
{
|
||||
"title" => "title",
|
||||
"author" => author,
|
||||
"image" => image,
|
||||
"date" => "2017-01-01",
|
||||
"description" => "description",
|
||||
"seo" => {
|
||||
"name" => "seo name",
|
||||
"date_modified" => "2017-01-02",
|
||||
"links" => %w(a b),
|
||||
},
|
||||
}
|
||||
end
|
||||
let(:config) do
|
||||
{
|
||||
"logo" => "logo",
|
||||
"timezone" => "America/New_York",
|
||||
}
|
||||
end
|
||||
let(:page) { make_page(metadata) }
|
||||
let(:site) { make_site(config) }
|
||||
let(:context) { make_context(:page => page, :site => site) }
|
||||
subject { Jekyll::SeoTag::Drop.new("", context).json_ld }
|
||||
|
||||
it "returns the context" do
|
||||
expect(subject).to have_key("@context")
|
||||
expect(subject["@context"]).to eql("http://schema.org")
|
||||
end
|
||||
|
||||
it "returns the type" do
|
||||
expect(subject).to have_key("@type")
|
||||
expect(subject["@type"]).to eql("BlogPosting")
|
||||
end
|
||||
|
||||
it "returns the name" do
|
||||
expect(subject).to have_key("name")
|
||||
expect(subject["name"]).to eql("seo name")
|
||||
end
|
||||
|
||||
it "returns the headline" do
|
||||
expect(subject).to have_key("headline")
|
||||
expect(subject["headline"]).to eql("title")
|
||||
end
|
||||
|
||||
context "author" do
|
||||
{
|
||||
"string" => "author",
|
||||
"hash" => { "name" => "author" },
|
||||
}.each do |key, value|
|
||||
context "when passed as a #{key}" do
|
||||
let(:author) { value }
|
||||
|
||||
it "returns the author" do
|
||||
expect(subject).to have_key("author")
|
||||
expect(subject["author"]).to be_a(Hash)
|
||||
expect(subject["author"]).to have_key("@type")
|
||||
expect(subject["author"]["@type"]).to eql("Person")
|
||||
expect(subject["author"]).to have_key("name")
|
||||
expect(subject["author"]["name"]).to be_a(String)
|
||||
expect(subject["author"]["name"]).to eql("author")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "image" do
|
||||
context "with image as a string" do
|
||||
let(:image) { "image" }
|
||||
|
||||
it "returns the image as a string" do
|
||||
expect(subject).to have_key("image")
|
||||
expect(subject["image"]).to be_a(String)
|
||||
expect(subject["image"]).to eql("/image")
|
||||
end
|
||||
end
|
||||
|
||||
context "with image as a hash" do
|
||||
let(:image) { { "path" => "image", "height" => 5, "width" => 10 } }
|
||||
|
||||
it "returns the image as a hash" do
|
||||
expect(subject).to have_key("image")
|
||||
expect(subject["image"]).to be_a(Hash)
|
||||
expect(subject["image"]).to eql({
|
||||
"@type" => "imageObject",
|
||||
"url" => "/image",
|
||||
"height" => 5,
|
||||
"width" => 10,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the datePublished" do
|
||||
expect(subject).to have_key("datePublished")
|
||||
expect(subject["datePublished"]).to eql("2017-01-01T00:00:00-05:00")
|
||||
end
|
||||
|
||||
it "returns the dateModified" do
|
||||
expect(subject).to have_key("dateModified")
|
||||
expect(subject["dateModified"]).to eql("2017-01-02T00:00:00-05:00")
|
||||
end
|
||||
|
||||
it "returns the description" do
|
||||
expect(subject).to have_key("description")
|
||||
expect(subject["description"]).to eql("description")
|
||||
end
|
||||
|
||||
it "returns the publisher" do
|
||||
expect(subject).to have_key("publisher")
|
||||
|
||||
publisher = subject["publisher"]
|
||||
expect(publisher).to be_a(Hash)
|
||||
|
||||
expect(publisher).to have_key("@type")
|
||||
expect(publisher["@type"]).to eql("Organization")
|
||||
expect(publisher).to have_key("logo")
|
||||
|
||||
logo = publisher["logo"]
|
||||
expect(logo).to have_key("@type")
|
||||
expect(logo["@type"]).to eql("ImageObject")
|
||||
expect(logo).to have_key("url")
|
||||
expect(logo["url"]).to eql("/logo")
|
||||
end
|
||||
|
||||
it "returns the main entity of page" do
|
||||
expect(subject).to have_key("mainEntityOfPage")
|
||||
expect(subject["mainEntityOfPage"]).to be_a(Hash)
|
||||
expect(subject["mainEntityOfPage"]).to have_key("@type")
|
||||
expect(subject["mainEntityOfPage"]["@type"]).to eql("WebPage")
|
||||
expect(subject["mainEntityOfPage"]).to have_key("@id")
|
||||
expect(subject["mainEntityOfPage"]["@id"]).to eql("/page.html")
|
||||
end
|
||||
|
||||
it "returns sameAs" do
|
||||
expect(subject).to have_key("sameAs")
|
||||
expect(subject["sameAs"]).to be_a(Array)
|
||||
expect(subject["sameAs"]).to eql(%w(a b))
|
||||
end
|
||||
|
||||
it "returns the url" do
|
||||
expect(subject).to have_key("url")
|
||||
expect(subject["url"]).to eql("/page.html")
|
||||
end
|
||||
end
|
|
@ -1,6 +1,4 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe Jekyll::SeoTag do
|
||||
RSpec.describe Jekyll::SeoTag do
|
||||
let(:page) { make_page }
|
||||
let(:site) { make_site }
|
||||
let(:post) { make_post }
|
||||
|
@ -195,10 +193,6 @@ describe Jekyll::SeoTag do
|
|||
expected = %r!<meta property="og:image" content="http://example.invalid/img/foo.png" />!
|
||||
expect(output).to match(expected)
|
||||
end
|
||||
|
||||
it "outputs the image JSON item" do
|
||||
expect(json_data["image"]).to eql("http://example.invalid/img/foo.png")
|
||||
end
|
||||
end
|
||||
|
||||
context "when given a facebook image" do
|
||||
|
@ -229,12 +223,6 @@ describe Jekyll::SeoTag do
|
|||
expected = %r!<meta property="og:image:width" content="2" />!
|
||||
expect(output).to match(expected)
|
||||
end
|
||||
|
||||
it "outputs the image JSON object with dimensions" do
|
||||
expect(json_data["image"]["url"]).to eql("http://example.invalid/img/foo.png")
|
||||
expect(json_data["image"]["height"]).to eql(1)
|
||||
expect(json_data["image"]["width"]).to eql(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -341,10 +329,8 @@ EOS
|
|||
end
|
||||
|
||||
it "minifies JSON-LD" do
|
||||
expected = <<-EOS
|
||||
{"@context": "http://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
"headline": "post",
|
||||
expected = <<-EOS.strip
|
||||
{"@context":"http://schema.org","@type":"BlogPosting","headline":"post",
|
||||
EOS
|
||||
expect(output).to match(expected)
|
||||
end
|
||||
|
@ -413,6 +399,7 @@ EOS
|
|||
|
||||
context "with the author in site.data.authors" do
|
||||
let(:author_data) { { "benbalter" => { "twitter" => "test" } } }
|
||||
|
||||
it "outputs the twitter card" do
|
||||
expected = %r!<meta name="twitter:creator" content="@test" />!
|
||||
expect(output).to match(expected)
|
Loading…
Reference in New Issue