mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-04-05 10:45:45 +00:00
Migrate test suite from RSpec to Minitest
This commit is contained in:
parent
e7190a35f6
commit
c65dee7e91
16 changed files with 370 additions and 397 deletions
3
Gemfile
3
Gemfile
|
|
@ -12,8 +12,7 @@ gem "bake", "~> 0.20"
|
|||
gem "nokogiri", "~> 1.18"
|
||||
|
||||
group :development, :test do
|
||||
gem "rspec", "~> 3.13"
|
||||
gem "guard", "~> 2.18"
|
||||
gem "guard-rspec", "~> 4.7"
|
||||
gem "minitest", "~> 6.0"
|
||||
gem "standard", "~> 1.43"
|
||||
end
|
||||
|
|
|
|||
25
Gemfile.lock
25
Gemfile.lock
|
|
@ -13,7 +13,6 @@ GEM
|
|||
fiber-annotation
|
||||
fiber-local (~> 1.1)
|
||||
json
|
||||
diff-lcs (1.6.2)
|
||||
dry-core (1.2.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
logger
|
||||
|
|
@ -63,11 +62,6 @@ GEM
|
|||
pry (>= 0.13.0)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rspec (4.7.3)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
ice_nine (0.11.2)
|
||||
io-console (0.8.2)
|
||||
json (2.18.1)
|
||||
|
|
@ -86,6 +80,8 @@ GEM
|
|||
mapping (1.1.3)
|
||||
method_source (1.1.0)
|
||||
mini_portile2 (2.8.9)
|
||||
minitest (6.0.0)
|
||||
prism (~> 1.5)
|
||||
nenv (0.3.0)
|
||||
nokogiri (1.19.0)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
|
|
@ -134,19 +130,6 @@ GEM
|
|||
io-console (~> 0.5)
|
||||
rexml (3.4.4)
|
||||
rouge (4.7.0)
|
||||
rspec (3.13.2)
|
||||
rspec-core (~> 3.13.0)
|
||||
rspec-expectations (~> 3.13.0)
|
||||
rspec-mocks (~> 3.13.0)
|
||||
rspec-core (3.13.6)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-expectations (3.13.5)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-mocks (3.13.7)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-support (3.13.7)
|
||||
rubocop (1.82.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
|
|
@ -194,7 +177,6 @@ PLATFORMS
|
|||
arm-linux-gnu
|
||||
arm-linux-musl
|
||||
arm64-darwin
|
||||
ruby
|
||||
x86-linux-gnu
|
||||
x86-linux-musl
|
||||
x86_64-darwin
|
||||
|
|
@ -206,13 +188,12 @@ DEPENDENCIES
|
|||
builder (~> 3.3)
|
||||
dry-struct (~> 1.8)
|
||||
guard (~> 2.18)
|
||||
guard-rspec (~> 4.7)
|
||||
kramdown (~> 2.5)
|
||||
kramdown-parser-gfm (~> 1.1)
|
||||
minitest (~> 6.0)
|
||||
nokogiri (~> 1.18)
|
||||
phlex (~> 2.3)
|
||||
rouge (~> 4.6)
|
||||
rspec (~> 3.13)
|
||||
standard (~> 1.43)
|
||||
|
||||
RUBY VERSION
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ Published posts in `posts/YYYY/MM/*.md` require YAML front matter keys:
|
|||
## Tests And Lint
|
||||
|
||||
```bash
|
||||
rbenv exec bundle exec rspec
|
||||
rbenv exec bundle exec bake test
|
||||
rbenv exec bundle exec standardrb
|
||||
```
|
||||
|
||||
|
|
|
|||
7
bake.rb
7
bake.rb
|
|
@ -136,9 +136,12 @@ def clean
|
|||
puts "Cleaned www/ directory"
|
||||
end
|
||||
|
||||
# Run RSpec tests
|
||||
# Run Minitest tests
|
||||
def test
|
||||
exec "bundle exec rspec"
|
||||
test_files = Dir.glob("spec/**/*_test.rb").sort
|
||||
abort "Error: no tests found in spec/**/*_test.rb" if test_files.empty?
|
||||
|
||||
exec "ruby", "-Ilib", "-Ispec", "-e", "ARGV.each { |file| require File.expand_path(file) }", *test_files
|
||||
end
|
||||
|
||||
# Run Guard for continuous testing
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
require "spec_helper"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe Pressa::Config::Loader do
|
||||
describe "#build_site" do
|
||||
it "builds a site from site.toml and projects.toml" do
|
||||
with_temp_config do |dir|
|
||||
loader = described_class.new(source_path: dir)
|
||||
site = loader.build_site
|
||||
|
||||
expect(site.author).to eq("Sami Samhuri")
|
||||
expect(site.url).to eq("https://samhuri.net")
|
||||
expect(site.image_url).to eq("https://samhuri.net/images/me.jpg")
|
||||
expect(site.styles.map(&:href)).to eq(["css/style.css"])
|
||||
|
||||
projects_plugin = site.plugins.find { |plugin| plugin.is_a?(Pressa::Projects::Plugin) }
|
||||
expect(projects_plugin).not_to be_nil
|
||||
expect(projects_plugin.scripts.map(&:src)).to eq(["js/projects.js"])
|
||||
end
|
||||
end
|
||||
|
||||
it "applies url_override and rewrites relative image_url with override host" do
|
||||
with_temp_config do |dir|
|
||||
loader = described_class.new(source_path: dir)
|
||||
site = loader.build_site(url_override: "https://beta.samhuri.net")
|
||||
|
||||
expect(site.url).to eq("https://beta.samhuri.net")
|
||||
expect(site.image_url).to eq("https://beta.samhuri.net/images/me.jpg")
|
||||
end
|
||||
end
|
||||
|
||||
it "raises a validation error for missing required site keys" do
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write(File.join(dir, "site.toml"), "title = \"x\"\n")
|
||||
File.write(File.join(dir, "projects.toml"), "")
|
||||
|
||||
loader = described_class.new(source_path: dir)
|
||||
expect { loader.build_site }.to raise_error(Pressa::Config::ValidationError, /Missing required site\.toml keys/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_temp_config
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write(File.join(dir, "site.toml"), <<~TOML)
|
||||
author = "Sami Samhuri"
|
||||
email = "sami@samhuri.net"
|
||||
title = "samhuri.net"
|
||||
description = "blog"
|
||||
url = "https://samhuri.net"
|
||||
image_url = "/images/me.jpg"
|
||||
scripts = []
|
||||
styles = ["css/style.css"]
|
||||
|
||||
[projects_plugin]
|
||||
scripts = ["js/projects.js"]
|
||||
styles = []
|
||||
TOML
|
||||
|
||||
File.write(File.join(dir, "projects.toml"), <<~TOML)
|
||||
[[projects]]
|
||||
name = "demo"
|
||||
title = "demo"
|
||||
description = "demo project"
|
||||
url = "https://github.com/samsonjs/demo"
|
||||
TOML
|
||||
|
||||
yield dir
|
||||
end
|
||||
end
|
||||
end
|
||||
73
spec/config/loader_test.rb
Normal file
73
spec/config/loader_test.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
require "test_helper"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
class Pressa::Config::LoaderTest < Minitest::Test
|
||||
def test_build_site_builds_a_site_from_site_toml_and_projects_toml
|
||||
with_temp_config do |dir|
|
||||
loader = Pressa::Config::Loader.new(source_path: dir)
|
||||
site = loader.build_site
|
||||
|
||||
assert_equal("Sami Samhuri", site.author)
|
||||
assert_equal("https://samhuri.net", site.url)
|
||||
assert_equal("https://samhuri.net/images/me.jpg", site.image_url)
|
||||
assert_equal(["css/style.css"], site.styles.map(&:href))
|
||||
|
||||
projects_plugin = site.plugins.find { |plugin| plugin.is_a?(Pressa::Projects::Plugin) }
|
||||
refute_nil(projects_plugin)
|
||||
assert_equal(["js/projects.js"], projects_plugin.scripts.map(&:src))
|
||||
end
|
||||
end
|
||||
|
||||
def test_build_site_applies_url_override_and_rewrites_relative_image_url_with_override_host
|
||||
with_temp_config do |dir|
|
||||
loader = Pressa::Config::Loader.new(source_path: dir)
|
||||
site = loader.build_site(url_override: "https://beta.samhuri.net")
|
||||
|
||||
assert_equal("https://beta.samhuri.net", site.url)
|
||||
assert_equal("https://beta.samhuri.net/images/me.jpg", site.image_url)
|
||||
end
|
||||
end
|
||||
|
||||
def test_build_site_raises_a_validation_error_for_missing_required_site_keys
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write(File.join(dir, "site.toml"), "title = \"x\"\n")
|
||||
File.write(File.join(dir, "projects.toml"), "")
|
||||
|
||||
loader = Pressa::Config::Loader.new(source_path: dir)
|
||||
error = assert_raises(Pressa::Config::ValidationError) { loader.build_site }
|
||||
assert_match(/Missing required site\.toml keys/, error.message)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_temp_config
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write(File.join(dir, "site.toml"), <<~TOML)
|
||||
author = "Sami Samhuri"
|
||||
email = "sami@samhuri.net"
|
||||
title = "samhuri.net"
|
||||
description = "blog"
|
||||
url = "https://samhuri.net"
|
||||
image_url = "/images/me.jpg"
|
||||
scripts = []
|
||||
styles = ["css/style.css"]
|
||||
|
||||
[projects_plugin]
|
||||
scripts = ["js/projects.js"]
|
||||
styles = []
|
||||
TOML
|
||||
|
||||
File.write(File.join(dir, "projects.toml"), <<~TOML)
|
||||
[[projects]]
|
||||
name = "demo"
|
||||
title = "demo"
|
||||
description = "demo project"
|
||||
url = "https://github.com/samsonjs/demo"
|
||||
TOML
|
||||
|
||||
yield dir
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
require "spec_helper"
|
||||
require "json"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe Pressa::Posts::JSONFeedWriter do
|
||||
let(:site) do
|
||||
Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
description: "blog",
|
||||
url: "https://samhuri.net",
|
||||
image_url: "https://samhuri.net/images/me.jpg"
|
||||
)
|
||||
end
|
||||
|
||||
let(:posts_by_year) { double("posts_by_year", recent_posts: [post]) }
|
||||
let(:writer) { described_class.new(site:, posts_by_year:) }
|
||||
|
||||
context "for link posts" do
|
||||
let(:post) do
|
||||
Pressa::Posts::Post.new(
|
||||
slug: "github-flow-like-a-pro",
|
||||
title: "GitHub Flow Like a Pro",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2015-05-28T07:42:27-07:00"),
|
||||
formatted_date: "28th May, 2015",
|
||||
link: "http://haacked.com/archive/2014/07/28/github-flow-aliases/",
|
||||
body: "<p>hello</p>",
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2015/05/github-flow-like-a-pro"
|
||||
)
|
||||
end
|
||||
|
||||
it "uses permalink as url and keeps external_url for destination links" do
|
||||
Dir.mktmpdir do |dir|
|
||||
writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
|
||||
expect(item.fetch("id")).to eq("https://samhuri.net/posts/2015/05/github-flow-like-a-pro")
|
||||
expect(item.fetch("url")).to eq("https://samhuri.net/posts/2015/05/github-flow-like-a-pro")
|
||||
expect(item.fetch("external_url")).to eq("http://haacked.com/archive/2014/07/28/github-flow-aliases/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "for regular posts" do
|
||||
let(:post) do
|
||||
Pressa::Posts::Post.new(
|
||||
slug: "swift-optional-or",
|
||||
title: "Swift Optional OR",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2017-10-01T10:00:00-07:00"),
|
||||
formatted_date: "1st October, 2017",
|
||||
body: "<p>hello</p>",
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2017/10/swift-optional-or"
|
||||
)
|
||||
end
|
||||
|
||||
it "omits external_url" do
|
||||
Dir.mktmpdir do |dir|
|
||||
writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
|
||||
expect(item.fetch("url")).to eq("https://samhuri.net/posts/2017/10/swift-optional-or")
|
||||
expect(item).not_to have_key("external_url")
|
||||
end
|
||||
end
|
||||
|
||||
it "expands root-relative links in content_html to absolute URLs" do
|
||||
post_with_assets = Pressa::Posts::Post.new(
|
||||
slug: "swift-optional-or",
|
||||
title: "Swift Optional OR",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2017-10-01T10:00:00-07:00"),
|
||||
formatted_date: "1st October, 2017",
|
||||
body: '<p><a href="/posts/2010/01/basics-of-the-mach-o-file-format">read</a></p>' \
|
||||
'<p><img src="/images/me.jpg" alt="me"></p>' \
|
||||
'<p><a href="//cdn.example.net/app.js">cdn</a></p>',
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2017/10/swift-optional-or"
|
||||
)
|
||||
allow(posts_by_year).to receive(:recent_posts).and_return([post_with_assets])
|
||||
|
||||
Dir.mktmpdir do |dir|
|
||||
writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
content_html = item.fetch("content_html")
|
||||
|
||||
expect(content_html).to include('href="https://samhuri.net/posts/2010/01/basics-of-the-mach-o-file-format"')
|
||||
expect(content_html).to include('src="https://samhuri.net/images/me.jpg"')
|
||||
expect(content_html).to include('href="//cdn.example.net/app.js"')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
115
spec/posts/json_feed_test.rb
Normal file
115
spec/posts/json_feed_test.rb
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
require "test_helper"
|
||||
require "json"
|
||||
require "tmpdir"
|
||||
|
||||
class Pressa::Posts::JSONFeedWriterTest < Minitest::Test
|
||||
class PostsByYearStub
|
||||
attr_accessor :posts
|
||||
|
||||
def initialize(posts)
|
||||
@posts = posts
|
||||
end
|
||||
|
||||
def recent_posts(_limit = 30)
|
||||
@posts
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@site = Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
description: "blog",
|
||||
url: "https://samhuri.net",
|
||||
image_url: "https://samhuri.net/images/me.jpg"
|
||||
)
|
||||
|
||||
@posts_by_year = PostsByYearStub.new([link_post])
|
||||
@writer = Pressa::Posts::JSONFeedWriter.new(site: @site, posts_by_year: @posts_by_year)
|
||||
end
|
||||
|
||||
def test_write_feed_for_link_posts_uses_permalink_as_url_and_keeps_external_url
|
||||
Dir.mktmpdir do |dir|
|
||||
@writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
|
||||
assert_equal("https://samhuri.net/posts/2015/05/github-flow-like-a-pro", item.fetch("id"))
|
||||
assert_equal("https://samhuri.net/posts/2015/05/github-flow-like-a-pro", item.fetch("url"))
|
||||
assert_equal("http://haacked.com/archive/2014/07/28/github-flow-aliases/", item.fetch("external_url"))
|
||||
end
|
||||
end
|
||||
|
||||
def test_write_feed_for_regular_posts_omits_external_url
|
||||
@posts_by_year.posts = [regular_post]
|
||||
|
||||
Dir.mktmpdir do |dir|
|
||||
@writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
|
||||
assert_equal("https://samhuri.net/posts/2017/10/swift-optional-or", item.fetch("url"))
|
||||
refute(item.key?("external_url"))
|
||||
end
|
||||
end
|
||||
|
||||
def test_write_feed_expands_root_relative_links_in_content_html
|
||||
@posts_by_year.posts = [post_with_assets]
|
||||
|
||||
Dir.mktmpdir do |dir|
|
||||
@writer.write_feed(target_path: dir, limit: 30)
|
||||
feed = JSON.parse(File.read(File.join(dir, "feed.json")))
|
||||
item = feed.fetch("items").first
|
||||
content_html = item.fetch("content_html")
|
||||
|
||||
assert_includes(content_html, 'href="https://samhuri.net/posts/2010/01/basics-of-the-mach-o-file-format"')
|
||||
assert_includes(content_html, 'src="https://samhuri.net/images/me.jpg"')
|
||||
assert_includes(content_html, 'href="//cdn.example.net/app.js"')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def link_post
|
||||
Pressa::Posts::Post.new(
|
||||
slug: "github-flow-like-a-pro",
|
||||
title: "GitHub Flow Like a Pro",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2015-05-28T07:42:27-07:00"),
|
||||
formatted_date: "28th May, 2015",
|
||||
link: "http://haacked.com/archive/2014/07/28/github-flow-aliases/",
|
||||
body: "<p>hello</p>",
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2015/05/github-flow-like-a-pro"
|
||||
)
|
||||
end
|
||||
|
||||
def regular_post
|
||||
Pressa::Posts::Post.new(
|
||||
slug: "swift-optional-or",
|
||||
title: "Swift Optional OR",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2017-10-01T10:00:00-07:00"),
|
||||
formatted_date: "1st October, 2017",
|
||||
body: "<p>hello</p>",
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2017/10/swift-optional-or"
|
||||
)
|
||||
end
|
||||
|
||||
def post_with_assets
|
||||
Pressa::Posts::Post.new(
|
||||
slug: "swift-optional-or",
|
||||
title: "Swift Optional OR",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("2017-10-01T10:00:00-07:00"),
|
||||
formatted_date: "1st October, 2017",
|
||||
body: '<p><a href="/posts/2010/01/basics-of-the-mach-o-file-format">read</a></p>' \
|
||||
'<p><img src="/images/me.jpg" alt="me"></p>' \
|
||||
'<p><a href="//cdn.example.net/app.js">cdn</a></p>',
|
||||
excerpt: "hello...",
|
||||
path: "/posts/2017/10/swift-optional-or"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
require "spec_helper"
|
||||
|
||||
RSpec.describe Pressa::Posts::PostMetadata do
|
||||
describe ".parse" do
|
||||
it "parses valid YAML front-matter" do
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Test Post
|
||||
Author: Trent Reznor
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
Tags: Ruby, Testing
|
||||
Scripts: highlight.js
|
||||
Styles: code.css
|
||||
Link: https://example.net/external
|
||||
---
|
||||
|
||||
This is the post body.
|
||||
MARKDOWN
|
||||
|
||||
metadata = described_class.parse(content)
|
||||
|
||||
expect(metadata.title).to eq("Test Post")
|
||||
expect(metadata.author).to eq("Trent Reznor")
|
||||
expect(metadata.formatted_date).to eq("5th November, 2025")
|
||||
expect(metadata.date.year).to eq(2025)
|
||||
expect(metadata.date.month).to eq(11)
|
||||
expect(metadata.date.day).to eq(5)
|
||||
expect(metadata.link).to eq("https://example.net/external")
|
||||
expect(metadata.tags).to eq(["Ruby", "Testing"])
|
||||
expect(metadata.scripts.map(&:src)).to eq(["js/highlight.js"])
|
||||
expect(metadata.styles.map(&:href)).to eq(["css/code.css"])
|
||||
end
|
||||
|
||||
it "raises error when required fields are missing" do
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Incomplete Post
|
||||
---
|
||||
|
||||
Body content
|
||||
MARKDOWN
|
||||
|
||||
expect {
|
||||
described_class.parse(content)
|
||||
}.to raise_error(/Missing required fields/)
|
||||
end
|
||||
|
||||
it "handles posts without optional fields" do
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Simple Post
|
||||
Author: Fat Mike
|
||||
Date: 1st January, 2025
|
||||
Timestamp: 2025-01-01T12:00:00-08:00
|
||||
---
|
||||
|
||||
Simple content
|
||||
MARKDOWN
|
||||
|
||||
metadata = described_class.parse(content)
|
||||
|
||||
expect(metadata.tags).to eq([])
|
||||
expect(metadata.scripts).to eq([])
|
||||
expect(metadata.styles).to eq([])
|
||||
expect(metadata.link).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
66
spec/posts/metadata_test.rb
Normal file
66
spec/posts/metadata_test.rb
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
require "test_helper"
|
||||
|
||||
class Pressa::Posts::PostMetadataTest < Minitest::Test
|
||||
def test_parse_parses_valid_yaml_front_matter
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Test Post
|
||||
Author: Trent Reznor
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
Tags: Ruby, Testing
|
||||
Scripts: highlight.js
|
||||
Styles: code.css
|
||||
Link: https://example.net/external
|
||||
---
|
||||
|
||||
This is the post body.
|
||||
MARKDOWN
|
||||
|
||||
metadata = Pressa::Posts::PostMetadata.parse(content)
|
||||
|
||||
assert_equal("Test Post", metadata.title)
|
||||
assert_equal("Trent Reznor", metadata.author)
|
||||
assert_equal("5th November, 2025", metadata.formatted_date)
|
||||
assert_equal(2025, metadata.date.year)
|
||||
assert_equal(11, metadata.date.month)
|
||||
assert_equal(5, metadata.date.day)
|
||||
assert_equal("https://example.net/external", metadata.link)
|
||||
assert_equal(["Ruby", "Testing"], metadata.tags)
|
||||
assert_equal(["js/highlight.js"], metadata.scripts.map(&:src))
|
||||
assert_equal(["css/code.css"], metadata.styles.map(&:href))
|
||||
end
|
||||
|
||||
def test_parse_raises_error_when_required_fields_are_missing
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Incomplete Post
|
||||
---
|
||||
|
||||
Body content
|
||||
MARKDOWN
|
||||
|
||||
error = assert_raises(StandardError) { Pressa::Posts::PostMetadata.parse(content) }
|
||||
assert_match(/Missing required fields/, error.message)
|
||||
end
|
||||
|
||||
def test_parse_handles_posts_without_optional_fields
|
||||
content = <<~MARKDOWN
|
||||
---
|
||||
Title: Simple Post
|
||||
Author: Fat Mike
|
||||
Date: 1st January, 2025
|
||||
Timestamp: 2025-01-01T12:00:00-08:00
|
||||
---
|
||||
|
||||
Simple content
|
||||
MARKDOWN
|
||||
|
||||
metadata = Pressa::Posts::PostMetadata.parse(content)
|
||||
|
||||
assert_equal([], metadata.tags)
|
||||
assert_equal([], metadata.scripts)
|
||||
assert_equal([], metadata.styles)
|
||||
assert_nil(metadata.link)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
require "spec_helper"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe Pressa::Posts::PostRepo do
|
||||
let(:repo) { described_class.new }
|
||||
|
||||
describe "#read_posts" do
|
||||
it "reads and organizes posts by year and month" do
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
posts_dir = File.join(tmpdir, "posts", "2025", "11")
|
||||
FileUtils.mkdir_p(posts_dir)
|
||||
|
||||
post_content = <<~MARKDOWN
|
||||
---
|
||||
Title: Shredding in November
|
||||
Author: Shaun White
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
---
|
||||
|
||||
Had an epic day at Whistler. The powder was deep and the lines were short.
|
||||
MARKDOWN
|
||||
|
||||
File.write(File.join(posts_dir, "shredding.md"), post_content)
|
||||
|
||||
posts_by_year = repo.read_posts(File.join(tmpdir, "posts"))
|
||||
|
||||
expect(posts_by_year.all_posts.length).to eq(1)
|
||||
|
||||
post = posts_by_year.all_posts.first
|
||||
expect(post.title).to eq("Shredding in November")
|
||||
expect(post.author).to eq("Shaun White")
|
||||
expect(post.slug).to eq("shredding")
|
||||
expect(post.year).to eq(2025)
|
||||
expect(post.month).to eq(11)
|
||||
expect(post.path).to eq("/posts/2025/11/shredding")
|
||||
end
|
||||
end
|
||||
|
||||
it "generates excerpts from post content" do
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
posts_dir = File.join(tmpdir, "posts", "2025", "11")
|
||||
FileUtils.mkdir_p(posts_dir)
|
||||
|
||||
post_content = <<~MARKDOWN
|
||||
---
|
||||
Title: Test Post
|
||||
Author: Greg Graffin
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
---
|
||||
|
||||
This is a test post with some content. It should generate an excerpt.
|
||||
|
||||

|
||||
|
||||
More content with a [link](https://example.net).
|
||||
MARKDOWN
|
||||
|
||||
File.write(File.join(posts_dir, "test.md"), post_content)
|
||||
|
||||
posts_by_year = repo.read_posts(File.join(tmpdir, "posts"))
|
||||
post = posts_by_year.all_posts.first
|
||||
|
||||
expect(post.excerpt).to include("test post")
|
||||
expect(post.excerpt).not_to include("![")
|
||||
expect(post.excerpt).to include("link")
|
||||
expect(post.excerpt).not_to include("[link]")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
73
spec/posts/repo_test.rb
Normal file
73
spec/posts/repo_test.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
require "test_helper"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
class Pressa::Posts::PostRepoTest < Minitest::Test
|
||||
def repo
|
||||
@repo ||= Pressa::Posts::PostRepo.new
|
||||
end
|
||||
|
||||
def test_read_posts_reads_and_organizes_posts_by_year_and_month
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
posts_dir = File.join(tmpdir, "posts", "2025", "11")
|
||||
FileUtils.mkdir_p(posts_dir)
|
||||
|
||||
post_content = <<~MARKDOWN
|
||||
---
|
||||
Title: Shredding in November
|
||||
Author: Shaun White
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
---
|
||||
|
||||
Had an epic day at Whistler. The powder was deep and the lines were short.
|
||||
MARKDOWN
|
||||
|
||||
File.write(File.join(posts_dir, "shredding.md"), post_content)
|
||||
|
||||
posts_by_year = repo.read_posts(File.join(tmpdir, "posts"))
|
||||
|
||||
assert_equal(1, posts_by_year.all_posts.length)
|
||||
|
||||
post = posts_by_year.all_posts.first
|
||||
assert_equal("Shredding in November", post.title)
|
||||
assert_equal("Shaun White", post.author)
|
||||
assert_equal("shredding", post.slug)
|
||||
assert_equal(2025, post.year)
|
||||
assert_equal(11, post.month)
|
||||
assert_equal("/posts/2025/11/shredding", post.path)
|
||||
end
|
||||
end
|
||||
|
||||
def test_read_posts_generates_excerpts_from_post_content
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
posts_dir = File.join(tmpdir, "posts", "2025", "11")
|
||||
FileUtils.mkdir_p(posts_dir)
|
||||
|
||||
post_content = <<~MARKDOWN
|
||||
---
|
||||
Title: Test Post
|
||||
Author: Greg Graffin
|
||||
Date: 5th November, 2025
|
||||
Timestamp: 2025-11-05T10:00:00-08:00
|
||||
---
|
||||
|
||||
This is a test post with some content. It should generate an excerpt.
|
||||
|
||||

|
||||
|
||||
More content with a [link](https://example.net).
|
||||
MARKDOWN
|
||||
|
||||
File.write(File.join(posts_dir, "test.md"), post_content)
|
||||
|
||||
posts_by_year = repo.read_posts(File.join(tmpdir, "posts"))
|
||||
post = posts_by_year.all_posts.first
|
||||
|
||||
assert_includes(post.excerpt, "test post")
|
||||
refute_includes(post.excerpt, "![")
|
||||
assert_includes(post.excerpt, "link")
|
||||
refute_includes(post.excerpt, "[link]")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
require "spec_helper"
|
||||
require "test_helper"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe Pressa::SiteGenerator do
|
||||
let(:site) do
|
||||
Pressa::Site.new(
|
||||
class Pressa::SiteGeneratorTest < Minitest::Test
|
||||
def site
|
||||
@site ||= Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
|
|
@ -15,23 +15,23 @@ RSpec.describe Pressa::SiteGenerator do
|
|||
)
|
||||
end
|
||||
|
||||
it "rejects a target path that matches the source path" do
|
||||
def test_rejects_a_target_path_that_matches_the_source_path
|
||||
Dir.mktmpdir do |dir|
|
||||
FileUtils.mkdir_p(File.join(dir, "public"))
|
||||
source_file = File.join(dir, "public", "keep.txt")
|
||||
File.write(source_file, "safe")
|
||||
|
||||
generator = described_class.new(site:)
|
||||
|
||||
expect {
|
||||
generator = Pressa::SiteGenerator.new(site:)
|
||||
error = assert_raises(ArgumentError) do
|
||||
generator.generate(source_path: dir, target_path: dir)
|
||||
}.to raise_error(ArgumentError, /must not be the same as or contain source_path/)
|
||||
end
|
||||
|
||||
expect(File.read(source_file)).to eq("safe")
|
||||
assert_match(/must not be the same as or contain source_path/, error.message)
|
||||
assert_equal("safe", File.read(source_file))
|
||||
end
|
||||
end
|
||||
|
||||
it "does not copy ignored dotfiles from public" do
|
||||
def test_does_not_copy_ignored_dotfiles_from_public
|
||||
Dir.mktmpdir do |dir|
|
||||
source_path = File.join(dir, "source")
|
||||
target_path = File.join(dir, "target")
|
||||
|
|
@ -42,11 +42,11 @@ RSpec.describe Pressa::SiteGenerator do
|
|||
File.write(File.join(public_path, ".gitkeep"), "")
|
||||
File.write(File.join(public_path, "visible.txt"), "ok")
|
||||
|
||||
described_class.new(site:).generate(source_path:, target_path:)
|
||||
Pressa::SiteGenerator.new(site:).generate(source_path:, target_path:)
|
||||
|
||||
expect(File.exist?(File.join(target_path, "visible.txt"))).to be(true)
|
||||
expect(File.exist?(File.join(target_path, ".DS_Store"))).to be(false)
|
||||
expect(File.exist?(File.join(target_path, ".gitkeep"))).to be(false)
|
||||
assert(File.exist?(File.join(target_path, "visible.txt")))
|
||||
refute(File.exist?(File.join(target_path, ".DS_Store")))
|
||||
refute(File.exist?(File.join(target_path, ".gitkeep")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
require_relative "../lib/pressa"
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.expect_with :rspec do |expectations|
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
end
|
||||
|
||||
config.mock_with :rspec do |mocks|
|
||||
mocks.verify_partial_doubles = true
|
||||
end
|
||||
|
||||
config.shared_context_metadata_behavior = :apply_to_host_groups
|
||||
config.filter_run_when_matching :focus
|
||||
config.example_status_persistence_file_path = "spec/examples.txt"
|
||||
config.disable_monkey_patching!
|
||||
config.warnings = true
|
||||
|
||||
if config.files_to_run.one?
|
||||
config.default_formatter = "doc"
|
||||
end
|
||||
|
||||
config.profile_examples = 10
|
||||
config.order = :random
|
||||
Kernel.srand config.seed
|
||||
end
|
||||
2
spec/test_helper.rb
Normal file
2
spec/test_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
require_relative "../lib/pressa"
|
||||
require "minitest/autorun"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
require "spec_helper"
|
||||
require "test_helper"
|
||||
|
||||
RSpec.describe Pressa::Views::Layout do
|
||||
let(:test_content_view) do
|
||||
class Pressa::Views::LayoutTest < Minitest::Test
|
||||
def test_content_view
|
||||
Class.new(Phlex::HTML) do
|
||||
def view_template
|
||||
article do
|
||||
|
|
@ -11,8 +11,8 @@ RSpec.describe Pressa::Views::Layout do
|
|||
end.new
|
||||
end
|
||||
|
||||
let(:site) do
|
||||
Pressa::Site.new(
|
||||
def site
|
||||
@site ||= Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
|
|
@ -21,31 +21,31 @@ RSpec.describe Pressa::Views::Layout do
|
|||
)
|
||||
end
|
||||
|
||||
it "renders child components as HTML instead of escaped text" do
|
||||
html = described_class.new(
|
||||
def test_rendering_child_components_as_html_instead_of_escaped_text
|
||||
html = Pressa::Views::Layout.new(
|
||||
site:,
|
||||
canonical_url: "https://samhuri.net/posts/",
|
||||
content: test_content_view
|
||||
).call
|
||||
|
||||
expect(html).to include("<article>")
|
||||
expect(html).to include("<h1>Hello</h1>")
|
||||
expect(html).not_to include("<article>")
|
||||
assert_includes(html, "<article>")
|
||||
assert_includes(html, "<h1>Hello</h1>")
|
||||
refute_includes(html, "<article>")
|
||||
end
|
||||
|
||||
it "keeps escaping enabled for untrusted string fields" do
|
||||
def test_keeps_escaping_enabled_for_untrusted_string_fields
|
||||
subtitle = "<img src=x onerror=alert(1)>"
|
||||
html = described_class.new(
|
||||
html = Pressa::Views::Layout.new(
|
||||
site:,
|
||||
canonical_url: "https://samhuri.net/posts/",
|
||||
page_subtitle: subtitle,
|
||||
content: test_content_view
|
||||
).call
|
||||
|
||||
expect(html).to include("<title>samhuri.net: <img src=x onerror=alert(1)></title>")
|
||||
assert_includes(html, "<title>samhuri.net: <img src=x onerror=alert(1)></title>")
|
||||
end
|
||||
|
||||
it "preserves absolute stylesheet URLs" do
|
||||
def test_preserves_absolute_stylesheet_urls
|
||||
cdn_site = Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
|
|
@ -55,12 +55,12 @@ RSpec.describe Pressa::Views::Layout do
|
|||
styles: [Pressa::Stylesheet.new(href: "https://cdn.example.com/site.css")]
|
||||
)
|
||||
|
||||
html = described_class.new(
|
||||
html = Pressa::Views::Layout.new(
|
||||
site: cdn_site,
|
||||
canonical_url: "https://samhuri.net/posts/",
|
||||
content: test_content_view
|
||||
).call
|
||||
|
||||
expect(html).to include(%(<link rel="stylesheet" type="text/css" href="https://cdn.example.com/site.css">))
|
||||
assert_includes(html, %(<link rel="stylesheet" type="text/css" href="https://cdn.example.com/site.css">))
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue