Feature: OG Social media cards
Not Original Gangsta, but ObjectGraph Reason: images were showing on LinkedIn, but small.
This commit is contained in:
@@ -28,6 +28,7 @@ defmodule Blogex.Post do
|
||||
:description,
|
||||
:date,
|
||||
:blog,
|
||||
:image,
|
||||
tags: [],
|
||||
published: true
|
||||
]
|
||||
@@ -39,6 +40,7 @@ defmodule Blogex.Post do
|
||||
body: String.t(),
|
||||
description: String.t(),
|
||||
date: Date.t(),
|
||||
image: String.t() | nil,
|
||||
tags: [String.t()],
|
||||
blog: atom(),
|
||||
published: boolean()
|
||||
|
||||
@@ -3,11 +3,33 @@ defmodule Blogex.SEO do
|
||||
SEO helpers for generating meta tags and sitemaps.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Returns a map of meta tag attributes for a blog listing page.
|
||||
Useful for setting OpenGraph and Twitter card tags on blog index pages.
|
||||
|
||||
<meta property="og:title" content={@meta.og_title} />
|
||||
"""
|
||||
def meta_tags_for_blog(blog_module, base_url) do
|
||||
url = "#{base_url}#{blog_module.base_path()}"
|
||||
|
||||
%{
|
||||
title: blog_module.title(),
|
||||
description: blog_module.description(),
|
||||
og_title: blog_module.title(),
|
||||
og_description: blog_module.description(),
|
||||
og_type: "website",
|
||||
og_url: url,
|
||||
og_image: nil,
|
||||
twitter_card: "summary"
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a map of meta tag attributes for a post.
|
||||
Useful for setting OpenGraph and Twitter card tags in your layout.
|
||||
|
||||
<meta property="og:title" content={@meta.og_title} />
|
||||
<meta property="og:image" content={@meta.og_image} />
|
||||
"""
|
||||
def meta_tags(post, base_url, blog_module) do
|
||||
url = "#{base_url}#{blog_module.base_path()}/#{post.id}"
|
||||
@@ -19,6 +41,7 @@ defmodule Blogex.SEO do
|
||||
og_description: post.description,
|
||||
og_type: "article",
|
||||
og_url: url,
|
||||
og_image: if(post.image, do: "#{base_url}#{post.image}", else: nil),
|
||||
article_published_time: Date.to_iso8601(post.date),
|
||||
article_author: post.author,
|
||||
article_tags: post.tags,
|
||||
|
||||
@@ -58,6 +58,20 @@ defmodule Blogex.PostTest do
|
||||
|
||||
assert post.published == false
|
||||
end
|
||||
|
||||
test "preserves image from frontmatter" do
|
||||
attrs = Map.put(valid_attrs(), :image, "/images/cover.png")
|
||||
|
||||
post = Post.build("x/2026/01-01-x.md", attrs, "<p>x</p>")
|
||||
|
||||
assert post.image == "/images/cover.png"
|
||||
end
|
||||
|
||||
test "defaults image to nil when not in frontmatter" do
|
||||
post = Post.build("x/2026/01-01-x.md", valid_attrs(), "<p>x</p>")
|
||||
|
||||
assert post.image == nil
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_attrs do
|
||||
|
||||
@@ -6,6 +6,8 @@ defmodule Blogex.SEOTest do
|
||||
|
||||
defmodule StubBlog do
|
||||
def base_path, do: "/blog/eng"
|
||||
def title, do: "Engineering Blog"
|
||||
def description, do: "Our engineering insights"
|
||||
end
|
||||
|
||||
@base_url "https://example.com"
|
||||
@@ -38,6 +40,63 @@ defmodule Blogex.SEOTest do
|
||||
assert meta.article_published_time == "2026-06-15"
|
||||
assert meta.article_tags == ["a", "b"]
|
||||
end
|
||||
|
||||
test "includes og_image when post has an image" do
|
||||
post = build(image: "/images/cover.png")
|
||||
|
||||
meta = SEO.meta_tags(post, @base_url, StubBlog)
|
||||
|
||||
assert meta.og_image == "https://example.com/images/cover.png"
|
||||
end
|
||||
|
||||
test "og_image is nil when post has no image" do
|
||||
post = build()
|
||||
|
||||
meta = SEO.meta_tags(post, @base_url, StubBlog)
|
||||
|
||||
assert meta.og_image == nil
|
||||
end
|
||||
|
||||
test "og_image uses absolute URL with base_url prefix" do
|
||||
post = build(image: "/assets/post-hero.jpg")
|
||||
|
||||
meta = SEO.meta_tags(post, "https://myapp.dev", StubBlog)
|
||||
|
||||
assert meta.og_image == "https://myapp.dev/assets/post-hero.jpg"
|
||||
end
|
||||
end
|
||||
|
||||
describe "meta_tags_for_blog/2" do
|
||||
test "returns website type" do
|
||||
meta = SEO.meta_tags_for_blog(StubBlog, @base_url)
|
||||
|
||||
assert meta.og_type == "website"
|
||||
end
|
||||
|
||||
test "includes blog title and description" do
|
||||
meta = SEO.meta_tags_for_blog(StubBlog, @base_url)
|
||||
|
||||
assert meta.og_title == "Engineering Blog"
|
||||
assert meta.og_description == "Our engineering insights"
|
||||
end
|
||||
|
||||
test "has no og_image for blog listing" do
|
||||
meta = SEO.meta_tags_for_blog(StubBlog, @base_url)
|
||||
|
||||
assert meta.og_image == nil
|
||||
end
|
||||
|
||||
test "builds correct blog URL" do
|
||||
meta = SEO.meta_tags_for_blog(StubBlog, @base_url)
|
||||
|
||||
assert meta.og_url == "https://example.com/blog/eng"
|
||||
end
|
||||
|
||||
test "uses summary twitter card for blog listing" do
|
||||
meta = SEO.meta_tags_for_blog(StubBlog, @base_url)
|
||||
|
||||
assert meta.twitter_card == "summary"
|
||||
end
|
||||
end
|
||||
|
||||
describe "sitemap/2" do
|
||||
|
||||
Reference in New Issue
Block a user