Revision f9044ac5c630809613d0d1c9e7c5f542dcc6826e authored by Yashu Mittal on 10 December 2017, 18:38:27 UTC, committed by jekyllbot on 10 December 2017, 18:38:27 UTC
Merge pull request 6617
1 parent fc783cd
test_site.rb
# frozen_string_literal: true
require "helper"
class TestSite < JekyllUnitTest
def with_image_as_post
tmp_image_path = File.join(source_dir, "_posts", "2017-09-01-jekyll-sticker.jpg")
FileUtils.cp File.join(Dir.pwd, "docs", "img", "jekyll-sticker.jpg"), tmp_image_path
yield
ensure
FileUtils.rm tmp_image_path
end
def read_posts
@site.posts.docs.concat(PostReader.new(@site).read_posts(""))
posts = Dir[source_dir("_posts", "**", "*")]
posts.delete_if do |post|
File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER)
end
end
context "configuring sites" do
should "have an array for plugins by default" do
site = Site.new default_configuration
assert_equal [File.join(Dir.pwd, "_plugins")], site.plugins
end
should "look for plugins under the site directory by default" do
site = Site.new(site_configuration)
assert_equal [source_dir("_plugins")], site.plugins
end
should "have an array for plugins if passed as a string" do
site = Site.new(site_configuration({ "plugins_dir" => "/tmp/plugins" }))
array = Utils::Platforms.windows? ? ["C:/tmp/plugins"] : ["/tmp/plugins"]
assert_equal array, site.plugins
end
should "have an array for plugins if passed as an array" do
site = Site.new(site_configuration({
"plugins_dir" => ["/tmp/plugins", "/tmp/otherplugins"],
}))
array = if Utils::Platforms.windows?
["C:/tmp/plugins", "C:/tmp/otherplugins"]
else
["/tmp/plugins", "/tmp/otherplugins"]
end
assert_equal array, site.plugins
end
should "have an empty array for plugins if nothing is passed" do
site = Site.new(site_configuration({ "plugins_dir" => [] }))
assert_equal [], site.plugins
end
should "have the default for plugins if nil is passed" do
site = Site.new(site_configuration({ "plugins_dir" => nil }))
assert_equal [source_dir("_plugins")], site.plugins
end
should "expose default baseurl" do
site = Site.new(default_configuration)
assert_equal Jekyll::Configuration::DEFAULTS["baseurl"], site.baseurl
end
should "expose baseurl passed in from config" do
site = Site.new(site_configuration({ "baseurl" => "/blog" }))
assert_equal "/blog", site.baseurl
end
should "only include theme includes_path if the path exists" do
site = fixture_site({ "theme" => "test-theme" })
assert_equal [source_dir("_includes"), theme_dir("_includes")],
site.includes_load_paths
allow(File).to receive(:directory?).with(theme_dir("_sass")).and_return(true)
allow(File).to receive(:directory?).with(theme_dir("_layouts")).and_return(true)
allow(File).to receive(:directory?).with(theme_dir("_includes")).and_return(false)
site = fixture_site({ "theme" => "test-theme" })
assert_equal [source_dir("_includes")], site.includes_load_paths
end
end
context "creating sites" do
setup do
@site = Site.new(site_configuration)
@num_invalid_posts = 4
end
teardown do
if defined?(MyGenerator)
self.class.send(:remove_const, :MyGenerator)
end
end
should "have an empty tag hash by default" do
assert_equal({}, @site.tags)
end
should "give site with parsed pages and posts to generators" do
class MyGenerator < Generator
def generate(site)
site.pages.dup.each do |page|
raise "#{page} isn't a page" unless page.is_a?(Page)
raise "#{page} doesn't respond to :name" unless page.respond_to?(:name)
end
site.file_read_opts[:secret_message] = "hi"
end
end
@site = Site.new(site_configuration)
@site.read
@site.generate
refute_equal 0, @site.pages.size
assert_equal "hi", @site.file_read_opts[:secret_message]
end
should "reset data before processing" do
clear_dest
@site.process
before_posts = @site.posts.length
before_layouts = @site.layouts.length
before_categories = @site.categories.length
before_tags = @site.tags.length
before_pages = @site.pages.length
before_static_files = @site.static_files.length
before_time = @site.time
@site.process
assert_equal before_posts, @site.posts.length
assert_equal before_layouts, @site.layouts.length
assert_equal before_categories, @site.categories.length
assert_equal before_tags, @site.tags.length
assert_equal before_pages, @site.pages.length
assert_equal before_static_files, @site.static_files.length
assert before_time <= @site.time
end
should "write only modified static files" do
clear_dest
StaticFile.reset_cache
@site.regenerator.clear
@site.process
some_static_file = @site.static_files[0].path
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# need to sleep because filesystem timestamps have best resolution in seconds
sleep 1
@site.process
mtime2 = File.stat(dest).mtime.to_i
assert_equal mtime1, mtime2
# simulate file modification by user
FileUtils.touch some_static_file
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
refute_equal mtime2, mtime3 # must be regenerated!
sleep 1
@site.process
mtime4 = File.stat(dest).mtime.to_i
assert_equal mtime3, mtime4 # no modifications, so must be the same
end
should "write static files if not modified but missing in destination" do
clear_dest
StaticFile.reset_cache
@site.regenerator.clear
@site.process
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# need to sleep because filesystem timestamps have best resolution in seconds
sleep 1
@site.process
mtime2 = File.stat(dest).mtime.to_i
assert_equal mtime1, mtime2
# simulate destination file deletion
File.unlink dest
refute File.exist?(dest)
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
assert_equal mtime2, mtime3 # must be regenerated and with original mtime!
sleep 1
@site.process
mtime4 = File.stat(dest).mtime.to_i
assert_equal mtime3, mtime4 # no modifications, so remain the same
end
should "setup plugins in priority order" do
assert_equal(
@site.converters.sort_by(&:class).map { |c| c.class.priority },
@site.converters.map { |c| c.class.priority }
)
assert_equal(
@site.generators.sort_by(&:class).map { |g| g.class.priority },
@site.generators.map { |g| g.class.priority }
)
end
should "sort pages alphabetically" do
method = Dir.method(:entries)
allow(Dir).to receive(:entries) do |*args, &block|
method.call(*args, &block).reverse
end
@site.process
# exclude files in symlinked directories here and insert them in the
# following step when not on Windows.
sorted_pages = %w(
%#\ +.md
.htaccess
about.html
application.coffee
bar.html
coffeescript.coffee
contacts.html
deal.with.dots.html
dynamic_file.php
environment.html
exploit.md
foo.md
humans.txt
index.html
index.html
info.md
main.scss
properties.html
sitemap.xml
static_files.html
)
unless Utils::Platforms.really_windows?
# files in symlinked directories may appear twice
sorted_pages.push("main.scss", "symlinked-file").sort!
end
assert_equal sorted_pages, @site.pages.map(&:name)
end
should "read posts" do
posts = read_posts
assert_equal posts.size - @num_invalid_posts, @site.posts.size
end
should "skip posts with invalid encoding" do
with_image_as_post do
posts = read_posts
num_invalid_posts = @num_invalid_posts + 1
assert_equal posts.size - num_invalid_posts, @site.posts.size
end
end
should "read pages with YAML front matter" do
abs_path = File.expand_path("about.html", @site.source)
assert_equal true, Utils.has_yaml_header?(abs_path)
end
should "enforce a strict 3-dash limit on the start of the YAML front matter" do
abs_path = File.expand_path("pgp.key", @site.source)
assert_equal false, Utils.has_yaml_header?(abs_path)
end
should "expose jekyll version to site payload" do
assert_equal Jekyll::VERSION, @site.site_payload["jekyll"]["version"]
end
should "expose list of static files to site payload" do
assert_equal @site.static_files, @site.site_payload["site"]["static_files"]
end
should "deploy payload" do
clear_dest
@site.process
posts = Dir[source_dir("**", "_posts", "**", "*")]
posts.delete_if do |post|
File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER)
end
categories = %w(
2013 bar baz category foo z_category MixedCase Mixedcase publish_test win
).sort
assert_equal posts.size - @num_invalid_posts, @site.posts.size
assert_equal categories, @site.categories.keys.sort
assert_equal 5, @site.categories["foo"].size
end
context "error handling" do
should "raise if destination is included in source" do
assert_raises Jekyll::Errors::FatalException do
Site.new(site_configuration("destination" => source_dir))
end
end
should "raise if destination is source" do
assert_raises Jekyll::Errors::FatalException do
Site.new(site_configuration("destination" => File.join(source_dir, "..")))
end
end
should "raise for bad frontmatter if strict_front_matter is set" do
site = Site.new(site_configuration(
"collections" => ["broken"],
"strict_front_matter" => true
))
assert_raises(Psych::SyntaxError) do
site.process
end
end
should "not raise for bad frontmatter if strict_front_matter is not set" do
site = Site.new(site_configuration(
"collections" => ["broken"],
"strict_front_matter" => false
))
site.process
end
end
context "with orphaned files in destination" do
setup do
clear_dest
@site.regenerator.clear
@site.process
# generate some orphaned files:
# single file
FileUtils.touch(dest_dir("obsolete.html"))
# single file in sub directory
FileUtils.mkdir(dest_dir("qux"))
FileUtils.touch(dest_dir("qux/obsolete.html"))
# empty directory
FileUtils.mkdir(dest_dir("quux"))
FileUtils.mkdir(dest_dir(".git"))
FileUtils.mkdir(dest_dir(".svn"))
FileUtils.mkdir(dest_dir(".hg"))
# single file in repository
FileUtils.touch(dest_dir(".git/HEAD"))
FileUtils.touch(dest_dir(".svn/HEAD"))
FileUtils.touch(dest_dir(".hg/HEAD"))
end
teardown do
FileUtils.rm_f(dest_dir("obsolete.html"))
FileUtils.rm_rf(dest_dir("qux"))
FileUtils.rm_f(dest_dir("quux"))
FileUtils.rm_rf(dest_dir(".git"))
FileUtils.rm_rf(dest_dir(".svn"))
FileUtils.rm_rf(dest_dir(".hg"))
end
should "remove orphaned files in destination" do
@site.process
refute_exist dest_dir("obsolete.html")
refute_exist dest_dir("qux")
refute_exist dest_dir("quux")
assert_exist dest_dir(".git")
assert_exist dest_dir(".git", "HEAD")
end
should "remove orphaned files in destination - keep_files .svn" do
config = site_configuration("keep_files" => %w(.svn))
@site = Site.new(config)
@site.process
refute_exist dest_dir(".htpasswd")
refute_exist dest_dir("obsolete.html")
refute_exist dest_dir("qux")
refute_exist dest_dir("quux")
refute_exist dest_dir(".git")
refute_exist dest_dir(".git", "HEAD")
assert_exist dest_dir(".svn")
assert_exist dest_dir(".svn", "HEAD")
end
end
context "using a non-default markdown processor in the configuration" do
should "use the non-default markdown processor" do
class Jekyll::Converters::Markdown::CustomMarkdown
def initialize(*args)
@args = args
end
def convert(*_args)
""
end
end
custom_processor = "CustomMarkdown"
s = Site.new(site_configuration("markdown" => custom_processor))
s.process
# Do some cleanup, we don't like straggling stuff.
Jekyll::Converters::Markdown.send(:remove_const, :CustomMarkdown)
end
should "ignore, if there are any bad characters in the class name" do
module Jekyll::Converters::Markdown::Custom
class Markdown
def initialize(*args)
@args = args
end
def convert(*_args)
""
end
end
end
bad_processor = "Custom::Markdown"
s = Site.new(site_configuration(
"markdown" => bad_processor,
"incremental" => false
))
assert_raises Jekyll::Errors::FatalException do
s.process
end
# Do some cleanup, we don't like straggling stuff.
Jekyll::Converters::Markdown.send(:remove_const, :Custom)
end
end
context "with an invalid markdown processor in the configuration" do
should "not throw an error at initialization time" do
bad_processor = "not a processor name"
assert Site.new(site_configuration("markdown" => bad_processor))
end
should "throw FatalException at process time" do
bad_processor = "not a processor name"
s = Site.new(site_configuration(
"markdown" => bad_processor,
"incremental" => false
))
assert_raises Jekyll::Errors::FatalException do
s.process
end
end
end
context "data directory" do
should "auto load yaml files" do
site = Site.new(site_configuration)
site.process
file_content = SafeYAML.load_file(File.join(source_dir, "_data", "members.yaml"))
assert_equal site.data["members"], file_content
assert_equal site.site_payload["site"]["data"]["members"], file_content
end
should "load yaml files from extracted method" do
site = Site.new(site_configuration)
site.process
file_content = DataReader.new(site)
.read_data_file(source_dir("_data", "members.yaml"))
assert_equal site.data["members"], file_content
assert_equal site.site_payload["site"]["data"]["members"], file_content
end
should "auto load yml files" do
site = Site.new(site_configuration)
site.process
file_content = SafeYAML.load_file(File.join(source_dir, "_data", "languages.yml"))
assert_equal site.data["languages"], file_content
assert_equal site.site_payload["site"]["data"]["languages"], file_content
end
should "auto load json files" do
site = Site.new(site_configuration)
site.process
file_content = SafeYAML.load_file(File.join(source_dir, "_data", "members.json"))
assert_equal site.data["members"], file_content
assert_equal site.site_payload["site"]["data"]["members"], file_content
end
should "auto load yaml files in subdirectory" do
site = Site.new(site_configuration)
site.process
file_content = SafeYAML.load_file(File.join(
source_dir, "_data", "categories", "dairy.yaml"
))
assert_equal site.data["categories"]["dairy"], file_content
assert_equal(
site.site_payload["site"]["data"]["categories"]["dairy"],
file_content
)
end
should "auto load yaml files in subdirectory with a period in the name" do
site = Site.new(site_configuration)
site.process
file_content = SafeYAML.load_file(File.join(
source_dir, "_data", "categories.01", "dairy.yaml"
))
assert_equal site.data["categories01"]["dairy"], file_content
assert_equal(
site.site_payload["site"]["data"]["categories01"]["dairy"],
file_content
)
end
should "load symlink files in unsafe mode" do
site = Site.new(site_configuration("safe" => false))
site.process
file_content = SafeYAML.load_file(File.join(source_dir, "_data", "products.yml"))
assert_equal site.data["products"], file_content
assert_equal site.site_payload["site"]["data"]["products"], file_content
end
should "load the symlink files in safe mode, " \
"as they resolve to inside site.source" do
site = Site.new(site_configuration("safe" => true))
site.process
file_content = SafeYAML.load_file(File.join(source_dir, "_data", "products.yml"))
assert_equal site.data["products"], file_content
assert_equal site.site_payload["site"]["data"]["products"], file_content
end
end
context "manipulating the Jekyll environment" do
setup do
@site = Site.new(site_configuration({
"incremental" => false,
}))
@site.process
@page = @site.pages.find { |p| p.name == "environment.html" }
end
should "default to 'development'" do
assert_equal "development", @page.content.strip
end
context "in production" do
setup do
ENV["JEKYLL_ENV"] = "production"
@site = Site.new(site_configuration({
"incremental" => false,
}))
@site.process
@page = @site.pages.find { |p| p.name == "environment.html" }
end
teardown do
ENV.delete("JEKYLL_ENV")
end
should "be overridden by JEKYLL_ENV" do
assert_equal "production", @page.content.strip
end
end
end
context "when setting theme" do
should "set no theme if config is not set" do
expect($stderr).not_to receive(:puts)
expect($stdout).not_to receive(:puts)
site = fixture_site({ "theme" => nil })
assert_nil site.theme
end
should "set no theme if config is a hash" do
output = capture_output do
site = fixture_site({ "theme" => {} })
assert_nil site.theme
end
expected_msg = "Theme: value of 'theme' in config should be String " \
"to use gem-based themes, but got Hash\n"
assert_includes output, expected_msg
end
should "set a theme if the config is a string" do
[:debug, :info, :warn, :error].each do |level|
expect(Jekyll.logger.writer).not_to receive(level)
end
site = fixture_site({ "theme" => "test-theme" })
assert_instance_of Jekyll::Theme, site.theme
assert_equal "test-theme", site.theme.name
end
end
context "with liquid profiling" do
setup do
@site = Site.new(site_configuration("profile" => true))
end
# Suppress output while testing
setup do
$stdout = StringIO.new
end
teardown do
$stdout = STDOUT
end
should "print profile table" do
expect(@site.liquid_renderer).to receive(:stats_table)
@site.process
end
end
context "incremental build" do
setup do
@site = Site.new(site_configuration({
"incremental" => true,
}))
@site.read
end
should "build incrementally" do
contacts_html = @site.pages.find { |p| p.name == "contacts.html" }
@site.process
source = @site.in_source_dir(contacts_html.path)
dest = File.expand_path(contacts_html.destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# need to sleep because filesystem timestamps have best resolution in seconds
sleep 1
@site.process
mtime2 = File.stat(dest).mtime.to_i
assert_equal mtime1, mtime2 # no modifications, so remain the same
# simulate file modification by user
FileUtils.touch source
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
refute_equal mtime2, mtime3 # must be regenerated
sleep 1
@site.process
mtime4 = File.stat(dest).mtime.to_i
assert_equal mtime3, mtime4 # no modifications, so remain the same
end
should "regenerate files that have had their destination deleted" do
contacts_html = @site.pages.find { |p| p.name == "contacts.html" }
@site.process
dest = File.expand_path(contacts_html.destination(@site.dest))
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
# simulate file modification by user
File.unlink dest
refute File.file?(dest)
sleep 1 # sleep for 1 second, since mtimes have 1s resolution
@site.process
assert File.file?(dest)
mtime2 = File.stat(dest).mtime.to_i
refute_equal mtime1, mtime2 # must be regenerated
end
end
end
end
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...