nesta 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitmodules +6 -0
- data/.travis.yml +9 -4
- data/CHANGES +18 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +70 -54
- data/LICENSE +1 -1
- data/RELEASING.md +5 -6
- data/Rakefile +20 -3
- data/lib/nesta/app.rb +4 -8
- data/lib/nesta/commands/command.rb +1 -2
- data/lib/nesta/commands/demo/content.rb +23 -5
- data/lib/nesta/commands/theme/install.rb +9 -7
- data/lib/nesta/helpers.rb +14 -0
- data/lib/nesta/models.rb +26 -22
- data/lib/nesta/navigation.rb +1 -1
- data/lib/nesta/version.rb +1 -1
- data/nesta.gemspec +10 -11
- data/templates/config/config.yml +1 -1
- data/{spec → test}/fixtures/nesta-plugin-test/Gemfile +0 -0
- data/{spec → test}/fixtures/nesta-plugin-test/Rakefile +0 -0
- data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test.rb +0 -0
- data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test/init.rb +0 -0
- data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test/version.rb +0 -0
- data/{spec → test}/fixtures/nesta-plugin-test/nesta-plugin-test.gemspec +0 -0
- data/test/integration/atom_feed_test.rb +178 -0
- data/test/integration/commands/demo/content_test.rb +31 -0
- data/test/integration/commands/edit_test.rb +21 -0
- data/test/integration/commands/new_test.rb +120 -0
- data/test/integration/commands/plugin/create_test.rb +128 -0
- data/test/integration/commands/theme/create_test.rb +35 -0
- data/test/integration/commands/theme/enable_test.rb +22 -0
- data/test/integration/commands/theme/install_test.rb +62 -0
- data/test/integration/default_theme_test.rb +220 -0
- data/test/integration/overrides_test.rb +118 -0
- data/test/integration/route_handlers_test.rb +96 -0
- data/test/integration/sitemap_test.rb +85 -0
- data/test/integration_test_helper.rb +61 -0
- data/test/support/model_factory.rb +169 -0
- data/test/support/silence_commands_during_tests.rb +5 -0
- data/test/support/temporary_files.rb +33 -0
- data/test/support/test_configuration.rb +19 -0
- data/test/test_helper.rb +26 -0
- data/test/unit/commands_test.rb +23 -0
- data/test/unit/config_test.rb +138 -0
- data/test/unit/file_model_test.rb +71 -0
- data/test/unit/menu_test.rb +82 -0
- data/test/unit/page_test.rb +571 -0
- data/test/unit/path_test.rb +41 -0
- data/test/unit/plugin_test.rb +47 -0
- data/views/master.sass +1 -1
- metadata +81 -85
- data/smoke-test.sh +0 -107
- data/spec/atom_spec.rb +0 -141
- data/spec/commands/demo/content_spec.rb +0 -65
- data/spec/commands/edit_spec.rb +0 -27
- data/spec/commands/new_spec.rb +0 -88
- data/spec/commands/plugin/create_spec.rb +0 -97
- data/spec/commands/system_spec.rb +0 -25
- data/spec/commands/theme/create_spec.rb +0 -41
- data/spec/commands/theme/enable_spec.rb +0 -44
- data/spec/commands/theme/install_spec.rb +0 -56
- data/spec/config_spec.rb +0 -127
- data/spec/model_factory.rb +0 -92
- data/spec/models_spec.rb +0 -700
- data/spec/overrides_spec.rb +0 -132
- data/spec/page_spec.rb +0 -560
- data/spec/path_spec.rb +0 -28
- data/spec/plugin_spec.rb +0 -51
- data/spec/sitemap_spec.rb +0 -105
- data/spec/spec_helper.rb +0 -114
@@ -0,0 +1,19 @@
|
|
1
|
+
module TestConfiguration
|
2
|
+
include TemporaryFiles
|
3
|
+
|
4
|
+
def stub_config(config, &block)
|
5
|
+
Nesta::Config.stub(:yaml_exists?, true) do
|
6
|
+
Nesta::Config.stub(:yaml_conf, config) do
|
7
|
+
yield
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def temp_content
|
13
|
+
{ 'content' => temp_path('content') }
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_temp_content_directory(&block)
|
17
|
+
stub_config(temp_content) { yield }
|
18
|
+
end
|
19
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'minitest/reporters'
|
5
|
+
|
6
|
+
reporter_setting = ENV.fetch('REPORTER', 'progress')
|
7
|
+
camel_case = reporter_setting.split(/_/).map { |word| word.capitalize }.join('')
|
8
|
+
Minitest::Reporters.use! Minitest::Reporters.const_get("#{camel_case}Reporter").new
|
9
|
+
|
10
|
+
require File.expand_path('../lib/nesta/env', File.dirname(__FILE__))
|
11
|
+
require File.expand_path('../lib/nesta/app', File.dirname(__FILE__))
|
12
|
+
|
13
|
+
require_relative 'support/model_factory'
|
14
|
+
require_relative 'support/temporary_files'
|
15
|
+
require_relative 'support/test_configuration'
|
16
|
+
|
17
|
+
Nesta::App.environment = 'test'
|
18
|
+
|
19
|
+
class Minitest::Test
|
20
|
+
def with_app_root(path, &block)
|
21
|
+
original, Nesta::App.root = Nesta::App.root, path
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
Nesta::App.root = original
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require_relative '../../lib/nesta/commands'
|
3
|
+
|
4
|
+
class TestCommand
|
5
|
+
include Nesta::Commands::Command
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'Nesta::Commands::Command' do
|
9
|
+
describe '#run_process' do
|
10
|
+
it 'catches errors when running external processes' do
|
11
|
+
TestCommand.new.run_process('ls / >/dev/null')
|
12
|
+
begin
|
13
|
+
stderr, $stderr = $stderr, File.open('/dev/null', 'w')
|
14
|
+
assert_raises(SystemExit) do
|
15
|
+
TestCommand.new.run_process('ls no-such-file 2>/dev/null')
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
$stderr.close
|
19
|
+
$stderr = stderr
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Nesta::Config do
|
4
|
+
include TestConfiguration
|
5
|
+
|
6
|
+
after do
|
7
|
+
ENV.keys.each { |variable| ENV.delete(variable) if variable =~ /NESTA_/ }
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns default value for "Read more"' do
|
11
|
+
assert_equal 'Continue reading', Nesta::Config.read_more
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns nil for author when not defined' do
|
15
|
+
assert_nil Nesta::Config.author
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'when settings defined in ENV' do
|
19
|
+
before do
|
20
|
+
@title = 'Title from ENV'
|
21
|
+
ENV['NESTA_TITLE'] = @title
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'falls back to config.yml' do
|
25
|
+
stub_config('subtitle' => 'Subtitle in YAML file') do
|
26
|
+
assert_equal 'Subtitle in YAML file', Nesta::Config.subtitle
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'overrides config.yml' do
|
31
|
+
stub_config('title' => 'Title in YAML file') do
|
32
|
+
assert_equal @title, Nesta::Config.title
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'knows how to cope with boolean values' do
|
37
|
+
Nesta::Config.settings << 'a_boolean'
|
38
|
+
begin
|
39
|
+
ENV['NESTA_A_BOOLEAN'] = 'true'
|
40
|
+
assert_equal true, Nesta::Config.a_boolean, 'should be true'
|
41
|
+
ENV['NESTA_A_BOOLEAN'] = 'false'
|
42
|
+
assert_equal false, Nesta::Config.a_boolean, 'should be false'
|
43
|
+
ensure
|
44
|
+
Nesta::Config.settings.pop
|
45
|
+
ENV.delete('NESTA_A_BOOLEAN')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should return configured value for "Read more"' do
|
50
|
+
ENV['NESTA_READ_MORE'] = 'Read on'
|
51
|
+
begin
|
52
|
+
assert_equal 'Read on', Nesta::Config.read_more
|
53
|
+
ensure
|
54
|
+
ENV.delete('NESTA_READ_MORE')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets author hash from ENV' do
|
59
|
+
name = 'Name from ENV'
|
60
|
+
uri = 'URI from ENV'
|
61
|
+
ENV['NESTA_AUTHOR__NAME'] = name
|
62
|
+
ENV['NESTA_AUTHOR__URI'] = uri
|
63
|
+
assert_equal name, Nesta::Config.author['name']
|
64
|
+
assert_equal uri, Nesta::Config.author['uri']
|
65
|
+
assert Nesta::Config.author['email'].nil?, 'should be nil'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'when settings only defined in config.yml' do
|
70
|
+
before do
|
71
|
+
@title = 'Title in YAML file'
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'reads configuration from YAML' do
|
75
|
+
stub_config('subtitle' => @title) do
|
76
|
+
assert_equal @title, Nesta::Config.subtitle
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sets author hash from YAML' do
|
81
|
+
name = 'Name from YAML'
|
82
|
+
uri = 'URI from YAML'
|
83
|
+
stub_config('author' => { 'name' => name, 'uri' => uri }) do
|
84
|
+
assert_equal name, Nesta::Config.author['name']
|
85
|
+
assert_equal uri, Nesta::Config.author['uri']
|
86
|
+
assert Nesta::Config.author['email'].nil?, 'should be nil'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'overrides top level settings with environment specific settings' do
|
91
|
+
config = {
|
92
|
+
'content' => 'general/path',
|
93
|
+
'test' => { 'content' => 'rack_env_specific/path' }
|
94
|
+
}
|
95
|
+
stub_config(config) do
|
96
|
+
assert_equal 'rack_env_specific/path', Nesta::Config.content
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'Nesta::Config.fetch' do
|
102
|
+
it 'retrieves settings from environment' do
|
103
|
+
ENV['NESTA_MY_SETTING'] = 'value in ENV'
|
104
|
+
begin
|
105
|
+
assert_equal 'value in ENV', Nesta::Config.fetch('my_setting')
|
106
|
+
assert_equal 'value in ENV', Nesta::Config.fetch(:my_setting)
|
107
|
+
ensure
|
108
|
+
ENV.delete('NESTA_MY_SETTING')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'retrieves settings from YAML' do
|
113
|
+
stub_config('my_setting' => 'value in YAML') do
|
114
|
+
assert_equal 'value in YAML', Nesta::Config.fetch('my_setting')
|
115
|
+
assert_equal 'value in YAML', Nesta::Config.fetch(:my_setting)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "throws NotDefined if a setting isn't defined" do
|
120
|
+
assert_raises(Nesta::Config::NotDefined) do
|
121
|
+
Nesta::Config.fetch('no such setting')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'allows default values to be set' do
|
126
|
+
assert_equal 'default', Nesta::Config.fetch('no such setting', 'default')
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'copes with non-truthy boolean values' do
|
130
|
+
ENV['NESTA_SETTING'] = 'false'
|
131
|
+
begin
|
132
|
+
assert_equal false, Nesta::Config.fetch('setting')
|
133
|
+
ensure
|
134
|
+
ENV.delete('NESTA_SETTING')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Nesta::FileModel do
|
4
|
+
include ModelFactory
|
5
|
+
include TestConfiguration
|
6
|
+
|
7
|
+
after do
|
8
|
+
Nesta::FileModel.purge_cache
|
9
|
+
remove_temp_directory
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can find all files of this type' do
|
13
|
+
with_temp_content_directory do
|
14
|
+
model = create(:file_model)
|
15
|
+
assert_equal [model], Nesta::FileModel.find_all
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.find_file_for_path' do
|
20
|
+
it 'returns filename for path' do
|
21
|
+
with_temp_content_directory do
|
22
|
+
model = create(:file_model)
|
23
|
+
filename = Nesta::FileModel.find_file_for_path(model.path)
|
24
|
+
assert_equal filename, model.filename
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns nil if file not found' do
|
29
|
+
with_temp_content_directory do
|
30
|
+
assert_nil Nesta::FileModel.find_file_for_path('foobar')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can parse metadata at top of a file' do
|
36
|
+
with_temp_content_directory do
|
37
|
+
model = create(:file_model)
|
38
|
+
metadata = model.parse_metadata('My key: some value')
|
39
|
+
assert_equal 'some value', metadata['my key']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "doesn't break loading pages with badly formatted metadata" do
|
44
|
+
with_temp_content_directory do
|
45
|
+
dodgy_metadata = "Key: value\nKey without value\nAnother key: value"
|
46
|
+
page = create(:page) do |path|
|
47
|
+
text = File.read(path)
|
48
|
+
File.open(path, 'w') do |file|
|
49
|
+
file.puts(dodgy_metadata)
|
50
|
+
file.write(text)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
Nesta::Page.find_by_path(page.path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'invalidates cached models when files are modified' do
|
58
|
+
with_temp_content_directory do
|
59
|
+
create(:file_model, path: 'a-page', metadata: { 'Version' => '1' })
|
60
|
+
now = Time.now
|
61
|
+
File.stub(:mtime, now - 1) do
|
62
|
+
Nesta::FileModel.load('a-page')
|
63
|
+
end
|
64
|
+
create(:file_model, path: 'a-page', metadata: { 'Version' => '2' })
|
65
|
+
model = File.stub(:mtime, now) do
|
66
|
+
Nesta::FileModel.load('a-page')
|
67
|
+
end
|
68
|
+
assert_equal '2', model.metadata('version')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Nesta::Menu do
|
4
|
+
include ModelFactory
|
5
|
+
include TestConfiguration
|
6
|
+
|
7
|
+
def with_page(&block)
|
8
|
+
with_temp_content_directory do
|
9
|
+
page = create(:page)
|
10
|
+
create_menu(page.path)
|
11
|
+
yield(page)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_hierarchy_of_pages(&block)
|
16
|
+
with_temp_content_directory do
|
17
|
+
pages = (1..6).map { |i| create(:page) }
|
18
|
+
text = <<-EOF
|
19
|
+
#{pages[0].path}
|
20
|
+
#{pages[1].path}
|
21
|
+
#{pages[2].path}
|
22
|
+
#{pages[3].path}
|
23
|
+
"no-such-page"
|
24
|
+
"another-missing-page"
|
25
|
+
#{pages[4].path}
|
26
|
+
#{pages[5].path}
|
27
|
+
EOF
|
28
|
+
create_menu(text)
|
29
|
+
yield(pages)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
remove_temp_directory
|
35
|
+
Nesta::FileModel.purge_cache
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'retrieves Page objects for the menu' do
|
39
|
+
with_page do |page|
|
40
|
+
assert_equal [page], Nesta::Menu.full_menu
|
41
|
+
assert_equal [page], Nesta::Menu.for_path('/')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "filters pages that don't exist out of the menu" do
|
46
|
+
with_temp_content_directory do
|
47
|
+
page = create(:page)
|
48
|
+
text = ['no-such-page', page.path].join("\n")
|
49
|
+
create_menu(text)
|
50
|
+
assert_equal [page], Nesta::Menu.top_level
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'nested sub menus' do
|
55
|
+
it 'returns top level menu items' do
|
56
|
+
with_hierarchy_of_pages do |pages|
|
57
|
+
assert_equal [pages[0], pages[4]], Nesta::Menu.top_level
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'returns full tree of menu items' do
|
62
|
+
with_hierarchy_of_pages do |pages|
|
63
|
+
page1, page2, page3, page4, page5, page6 = pages
|
64
|
+
expected = [page1, [page2, [page3, page4]], page5, [page6]]
|
65
|
+
assert_equal expected, Nesta::Menu.full_menu
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns part of the tree of menu items' do
|
70
|
+
with_hierarchy_of_pages do |pages|
|
71
|
+
page1, page2, page3, page4, page5, page6 = pages
|
72
|
+
assert_equal [page2, [page3, page4]], Nesta::Menu.for_path(page2.path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'deems menu for path not in menu to be nil' do
|
77
|
+
with_hierarchy_of_pages do
|
78
|
+
assert_nil Nesta::Menu.for_path('wibble')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,571 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Nesta::Page do
|
4
|
+
include ModelFactory
|
5
|
+
include TestConfiguration
|
6
|
+
|
7
|
+
after do
|
8
|
+
Nesta::FileModel.purge_cache
|
9
|
+
remove_temp_directory
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises error if instantiated for non existant file' do
|
13
|
+
with_temp_content_directory do
|
14
|
+
assert_raises(Sinatra::NotFound) do
|
15
|
+
Nesta::Page.new('no-such-file')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.find_by_path' do
|
21
|
+
it 'finds model instances by path' do
|
22
|
+
with_temp_content_directory do
|
23
|
+
page = create(:page)
|
24
|
+
assert_equal page.heading, Nesta::Page.find_by_path(page.path).heading
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'finds model for index page by path' do
|
29
|
+
with_temp_content_directory do
|
30
|
+
page = create(:page, path: 'path/index')
|
31
|
+
assert_equal page.heading, Nesta::Page.find_by_path('path').heading
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'finds model for home page when path is /' do
|
36
|
+
with_temp_content_directory do
|
37
|
+
create(:page, heading: 'Home', path: 'index')
|
38
|
+
assert_equal 'Home', Nesta::Page.find_by_path('/').title
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns nil if page not found' do
|
43
|
+
with_temp_content_directory do
|
44
|
+
assert_nil Nesta::Page.find_by_path('no-such-page')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns nil for draft pages when running in production'do
|
49
|
+
with_temp_content_directory do
|
50
|
+
draft = create(:page, metadata: { 'flags' => 'draft' })
|
51
|
+
assert Nesta::Page.find_by_path(draft.path), 'should find draft'
|
52
|
+
Nesta::App.stub(:production?, true) do
|
53
|
+
assert_nil Nesta::Page.find_by_path(draft.path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.find_articles 'do
|
60
|
+
it "doesn't return articles with a published date in the future" do
|
61
|
+
with_temp_content_directory do
|
62
|
+
future_date = (Time.now + 172800).strftime('%d %B %Y')
|
63
|
+
article = create(:article, metadata: { 'date' => future_date })
|
64
|
+
assert_nil Nesta::Page.find_articles.detect { |a| a == article }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "doesn't return pages without a date" do
|
69
|
+
with_temp_content_directory do
|
70
|
+
create(:category)
|
71
|
+
create(:article)
|
72
|
+
articles = Nesta::Page.find_articles
|
73
|
+
assert_equal 1, articles.size
|
74
|
+
articles.each { |page| fail 'not an article' if page.date.nil? }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns articles in reverse chronological order' do
|
79
|
+
with_temp_content_directory do
|
80
|
+
create(:article, metadata: { 'date' => '30 December 2008' })
|
81
|
+
create(:article, metadata: { 'date' => '31 December 2008' })
|
82
|
+
article1, article2 = Nesta::Page.find_articles[0..1]
|
83
|
+
assert article1.date > article2.date, 'not reverse chronological order'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#title' do
|
89
|
+
it 'returns page heading by default' do
|
90
|
+
with_temp_content_directory do
|
91
|
+
page = create(:page, heading: 'Heading')
|
92
|
+
assert_equal page.heading, Nesta::Page.find_by_path(page.path).title
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'overrides heading with title set in metadata' do
|
97
|
+
with_temp_content_directory do
|
98
|
+
page = create(:page, metadata: { 'title' => 'Specific title' })
|
99
|
+
assert_equal 'Specific title', Nesta::Page.find_by_path(page.path).title
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'defaults to site title for home page' do
|
104
|
+
stub_config(temp_content.merge('title' => 'Site title')) do
|
105
|
+
create(:page, heading: nil, path: 'index')
|
106
|
+
assert_equal 'Site title', Nesta::Page.find_by_path('/').title
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#heading' do
|
112
|
+
it 'raises error if heading not set' do
|
113
|
+
with_temp_content_directory do
|
114
|
+
assert_raises Nesta::HeadingNotSet do
|
115
|
+
create(:page, heading: nil).heading
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'parses Markdown pages returning contents of first # heading' do
|
121
|
+
with_temp_content_directory do
|
122
|
+
page = create(:page) do |path|
|
123
|
+
file = File.open(path, 'w')
|
124
|
+
file.write('# Hello Markdown')
|
125
|
+
file.close
|
126
|
+
end
|
127
|
+
assert_equal 'Hello Markdown', page.heading
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'parses Textile pages returning contents of first h1. heading' do
|
132
|
+
with_temp_content_directory do
|
133
|
+
page = create(:page, ext: 'textile') do |path|
|
134
|
+
file = File.open(path, 'w')
|
135
|
+
file.write('h1. Hello Textile')
|
136
|
+
file.close
|
137
|
+
end
|
138
|
+
assert_equal 'Hello Textile', page.heading
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'parases Haml pages returning contents of first %h1 tag' do
|
143
|
+
with_temp_content_directory do
|
144
|
+
page = create(:page, ext: 'haml') do |path|
|
145
|
+
file = File.open(path, 'w')
|
146
|
+
file.write('%h1 Hello Haml')
|
147
|
+
file.close
|
148
|
+
end
|
149
|
+
assert_equal 'Hello Haml', page.heading
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'ignores subsequent h1 tags' do
|
154
|
+
with_temp_content_directory do
|
155
|
+
page = create(:page, content: '# Second heading')
|
156
|
+
fail 'wrong h1 tag' if page.heading == 'Second heading'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'ignores trailing # characters in Markdown headings' do
|
161
|
+
with_temp_content_directory do
|
162
|
+
page = create(:page, heading: 'With trailing #')
|
163
|
+
assert_equal 'With trailing', page.heading
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#abspath' do
|
169
|
+
it 'returns / for home page' do
|
170
|
+
with_temp_content_directory do
|
171
|
+
create(:page, path: 'index')
|
172
|
+
assert_equal '/', Nesta::Page.find_by_path('index').abspath
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#permalink' do
|
178
|
+
it 'returns basename of filename' do
|
179
|
+
with_temp_content_directory do
|
180
|
+
assert_equal 'page', create(:page, path: 'path/to/page').permalink
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'returns empty string for home page' do
|
185
|
+
with_temp_content_directory do
|
186
|
+
home = create(:page, path: 'index')
|
187
|
+
assert_equal '', Nesta::Page.find_by_path(home.path).permalink
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'removes /index from permalink of index pages' do
|
192
|
+
with_temp_content_directory do
|
193
|
+
index = create(:page, path: 'parent/child/index')
|
194
|
+
assert_equal 'child', index.permalink
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe '#parent' do
|
200
|
+
it 'finds the parent by inspecting the path' do
|
201
|
+
with_temp_content_directory do
|
202
|
+
parent = create(:page, path: 'parent')
|
203
|
+
child = create(:page, path: 'parent/child')
|
204
|
+
assert_equal parent, child.parent
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'returns nil for pages at top level' do
|
209
|
+
with_temp_content_directory do
|
210
|
+
assert_nil create(:page, path: 'top-level').parent
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'finds parents that are index pages' do
|
215
|
+
with_temp_content_directory do
|
216
|
+
home = create(:page, path: 'index')
|
217
|
+
child = create(:page, path: 'parent')
|
218
|
+
assert_equal home, child.parent
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it "returns grandparent if parent doesn't exist" do
|
223
|
+
with_temp_content_directory do
|
224
|
+
grandparent = create(:page, path: 'grandparent')
|
225
|
+
child = create(:page, path: 'grandparent/parent/child')
|
226
|
+
assert_equal grandparent, child.parent
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'recognises that home page can be returned as a grandparent' do
|
231
|
+
with_temp_content_directory do
|
232
|
+
grandparent = create(:page, path: 'index')
|
233
|
+
child = create(:page, path: 'parent/child')
|
234
|
+
assert_equal grandparent, child.parent
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'returns nil if page has no parent' do
|
239
|
+
with_temp_content_directory do
|
240
|
+
assert_nil create(:page, path: 'index').parent
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'finds parent of an index page' do
|
245
|
+
with_temp_content_directory do
|
246
|
+
parent = create(:page, path: 'parent')
|
247
|
+
index = create(:page, path: 'parent/child/index')
|
248
|
+
assert_equal parent, index.parent
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe '#priority' do
|
254
|
+
it 'defaults to 0 for pages in category' do
|
255
|
+
with_temp_content_directory do
|
256
|
+
page = create(:page, metadata: { 'categories' => 'the-category' })
|
257
|
+
assert_equal 0, page.priority('the-category')
|
258
|
+
assert_nil page.priority('another-category')
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'parses metadata to determine priority in each category' do
|
263
|
+
with_temp_content_directory do
|
264
|
+
page = create(:page, metadata: {
|
265
|
+
'categories' => ' category-page:1, another-page , and-another :-1 '
|
266
|
+
})
|
267
|
+
assert_equal 1, page.priority('category-page')
|
268
|
+
assert_equal 0, page.priority('another-page')
|
269
|
+
assert_equal -1, page.priority('and-another')
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe '#pages' do
|
275
|
+
it 'returns pages but not articles within this category' do
|
276
|
+
with_temp_content_directory do
|
277
|
+
category = create(:category)
|
278
|
+
metadata = { 'categories' => category.path }
|
279
|
+
page1 = create(:category, metadata: metadata)
|
280
|
+
page2 = create(:category, metadata: metadata)
|
281
|
+
create(:article, metadata: metadata)
|
282
|
+
assert_equal [page1, page2], category.pages
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'sorts pages within a category by priority' do
|
287
|
+
with_temp_content_directory do
|
288
|
+
category = create(:category)
|
289
|
+
create(:category, metadata: { 'categories' => category.path })
|
290
|
+
page = create(:category, metadata: {
|
291
|
+
'categories' => "#{category.path}:1"
|
292
|
+
})
|
293
|
+
assert_equal 0, category.pages.index(page)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'orders pages within a category by heading if priority not set' do
|
298
|
+
with_temp_content_directory do
|
299
|
+
category = create(:category)
|
300
|
+
metadata = { 'categories' => category.path }
|
301
|
+
last = create(:category, heading: 'B', metadata: metadata)
|
302
|
+
first = create(:category, heading: 'A', metadata: metadata)
|
303
|
+
assert_equal [first, last], category.pages
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'filters out draft pages when running in production' do
|
308
|
+
with_temp_content_directory do
|
309
|
+
category = create(:category)
|
310
|
+
create(:page, metadata: {
|
311
|
+
'categories' => category.path,
|
312
|
+
'flags' => 'draft'
|
313
|
+
})
|
314
|
+
fail 'should include draft pages' if category.pages.empty?
|
315
|
+
Nesta::App.stub(:production?, true) do
|
316
|
+
assert category.pages.empty?, 'should filter out draft pages'
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe '#articles' do
|
323
|
+
it "returns just the articles that are in this page's category" do
|
324
|
+
with_temp_content_directory do
|
325
|
+
category1 = create(:category)
|
326
|
+
in_category1 = { 'categories' => category1.path }
|
327
|
+
category2 = create(:category, metadata: in_category1)
|
328
|
+
in_category2 = { 'categories' => category2.path }
|
329
|
+
|
330
|
+
article1 = create(:article, metadata: in_category1)
|
331
|
+
create(:article, metadata: in_category2)
|
332
|
+
|
333
|
+
assert_equal [article1], category1.articles
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'returns articles in reverse chronological order' do
|
338
|
+
with_temp_content_directory do
|
339
|
+
category = create(:category)
|
340
|
+
create(:article, metadata: {
|
341
|
+
'date' => '30 December 2008',
|
342
|
+
'categories' => category.path
|
343
|
+
})
|
344
|
+
latest = create(:article, metadata: {
|
345
|
+
'date' => '31 December 2008',
|
346
|
+
'categories' => category.path
|
347
|
+
})
|
348
|
+
assert_equal latest, category.articles.first
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe '#categories' do
|
354
|
+
it "returns a page's categories" do
|
355
|
+
with_temp_content_directory do
|
356
|
+
category1 = create(:category, path: 'path/to/cat1')
|
357
|
+
category2 = create(:category, path: 'path/to/cat2')
|
358
|
+
article = create(:article, metadata: {
|
359
|
+
'categories' => 'path/to/cat1, path/to/cat2'
|
360
|
+
})
|
361
|
+
assert_equal [category1, category2], article.categories
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'only returns categories that exist' do
|
366
|
+
with_temp_content_directory do
|
367
|
+
article = create(:article, metadata: { 'categories' => 'no-such-page' })
|
368
|
+
assert_empty article.categories
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
describe '#to_html' do
|
374
|
+
it 'produces no output if page has no content' do
|
375
|
+
with_temp_content_directory do
|
376
|
+
page = create(:page) do |path|
|
377
|
+
file = File.open(path, 'w')
|
378
|
+
file.close
|
379
|
+
end
|
380
|
+
assert_match /^\s*$/, Nesta::Page.find_by_path(page.path).to_html
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'converts page content to HTML' do
|
385
|
+
with_temp_content_directory do
|
386
|
+
assert_match %r{<h1>Hello</h1>}, create(:page, heading: 'Hello').to_html
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
it "doesn't include leading metadata in HTML" do
|
391
|
+
with_temp_content_directory do
|
392
|
+
page = create(:page, metadata: { 'key' => 'value' })
|
393
|
+
fail 'HTML contains metadata' if page.to_html =~ /(key|value)/
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
describe '#summary' do
|
399
|
+
it 'returns value set in metadata wrapped in p tags' do
|
400
|
+
with_temp_content_directory do
|
401
|
+
page = create(:page, metadata: { 'Summary' => 'Summary paragraph' })
|
402
|
+
assert_equal "<p>Summary paragraph</p>\n", page.summary
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'treats double newline characters as paragraph breaks' do
|
407
|
+
with_temp_content_directory do
|
408
|
+
page = create(:page, metadata: { 'Summary' => 'Line 1\n\nLine 2' })
|
409
|
+
assert_includes page.summary, '<p>Line 1</p>'
|
410
|
+
assert_includes page.summary, '<p>Line 2</p>'
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe '#body' do
|
416
|
+
it "doesn't include page heading" do
|
417
|
+
with_temp_content_directory do
|
418
|
+
page = create(:page, heading: 'Heading')
|
419
|
+
fail 'body contains heading' if page.body_markup =~ /#{page.heading}/
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
describe '#metadata' do
|
425
|
+
it 'return value of any key set in metadata at top of page' do
|
426
|
+
with_temp_content_directory do
|
427
|
+
page = create(:page, metadata: { 'Any key' => 'Any string' })
|
428
|
+
assert_equal 'Any string', page.metadata('Any key')
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
describe '#layout' do
|
434
|
+
it 'defaults to :layout' do
|
435
|
+
with_temp_content_directory do
|
436
|
+
assert_equal :layout, create(:page).layout
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'returns value set in metadata' do
|
441
|
+
with_temp_content_directory do
|
442
|
+
page = create(:page, metadata: { 'Layout' => 'my_layout' })
|
443
|
+
assert_equal :my_layout, page.layout
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
describe '#template' do
|
449
|
+
it 'defaults to :page' do
|
450
|
+
with_temp_content_directory do
|
451
|
+
assert_equal :page, create(:page).template
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'returns value set in metadata' do
|
456
|
+
with_temp_content_directory do
|
457
|
+
page = create(:page, metadata: { 'Template' => 'my_template' })
|
458
|
+
assert_equal :my_template, page.template
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe '#link_text' do
|
464
|
+
it 'raises error if neither heading nor link text set' do
|
465
|
+
with_temp_content_directory do
|
466
|
+
assert_raises Nesta::LinkTextNotSet do
|
467
|
+
create(:page, heading: nil).link_text
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'defaults to page heading' do
|
473
|
+
with_temp_content_directory do
|
474
|
+
page = create(:page)
|
475
|
+
assert_equal page.heading, page.link_text
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'returns value set in metadata' do
|
480
|
+
with_temp_content_directory do
|
481
|
+
page = create(:page, metadata: { 'link text' => 'Hello' })
|
482
|
+
assert_equal 'Hello', page.link_text
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
describe '#read_more' do
|
488
|
+
it 'has sensible default' do
|
489
|
+
with_temp_content_directory do
|
490
|
+
assert_equal 'Continue reading', create(:page).read_more
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
it 'returns value set in metadata' do
|
495
|
+
with_temp_content_directory do
|
496
|
+
page = create(:page, metadata: { 'Read more' => 'Click here' })
|
497
|
+
assert_equal 'Click here', page.read_more
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
describe '#description' do
|
503
|
+
it 'returns nil by default' do
|
504
|
+
with_temp_content_directory { assert_nil create(:page).description }
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'returns value set in metadata' do
|
508
|
+
with_temp_content_directory do
|
509
|
+
page = create(:page, metadata: { 'description' => 'A page' })
|
510
|
+
assert_equal 'A page', page.description
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
describe '#keywords' do
|
516
|
+
it 'returns nil by default' do
|
517
|
+
with_temp_content_directory { assert_nil create(:page).keywords }
|
518
|
+
end
|
519
|
+
|
520
|
+
it 'returns value set in metadata' do
|
521
|
+
with_temp_content_directory do
|
522
|
+
page = create(:page, metadata: { 'keywords' => 'good, content' })
|
523
|
+
assert_equal 'good, content', page.keywords
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
describe '#date' do
|
529
|
+
it 'returns date article was published' do
|
530
|
+
with_temp_content_directory do
|
531
|
+
article = create(:article, metadata: { 'date' => 'November 18 2015' })
|
532
|
+
assert_equal '18 November 2015', article.date.strftime('%d %B %Y')
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
describe '#flagged_as?' do
|
538
|
+
it 'returns true if flags metadata contains the string' do
|
539
|
+
with_temp_content_directory do
|
540
|
+
page = create(:page, metadata: { 'flags' => 'A flag, popular' })
|
541
|
+
assert page.flagged_as?('popular'), 'should be flagged popular'
|
542
|
+
assert page.flagged_as?('A flag'), 'should be flagged with "A flag"'
|
543
|
+
fail 'not flagged as green' if page.flagged_as?('green')
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
describe '#draft?' do
|
549
|
+
it 'returns true if page flagged as draft' do
|
550
|
+
with_temp_content_directory do
|
551
|
+
page = create(:page, metadata: { 'flags' => 'draft' })
|
552
|
+
assert page.draft?
|
553
|
+
fail 'not a draft' if create(:page).draft?
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
describe '#last_modified' do
|
559
|
+
it 'reads last modified timestamp from disk' do
|
560
|
+
with_temp_content_directory do
|
561
|
+
page = create(:page)
|
562
|
+
file_stat = Minitest::Mock.new
|
563
|
+
file_stat.expect(:mtime, Time.parse('3 January 2009'))
|
564
|
+
File.stub(:stat, file_stat) do
|
565
|
+
assert_equal '03 Jan 2009', page.last_modified.strftime('%d %b %Y')
|
566
|
+
end
|
567
|
+
file_stat.verify
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|