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.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.gitmodules +6 -0
  3. data/.travis.yml +9 -4
  4. data/CHANGES +18 -2
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +70 -54
  7. data/LICENSE +1 -1
  8. data/RELEASING.md +5 -6
  9. data/Rakefile +20 -3
  10. data/lib/nesta/app.rb +4 -8
  11. data/lib/nesta/commands/command.rb +1 -2
  12. data/lib/nesta/commands/demo/content.rb +23 -5
  13. data/lib/nesta/commands/theme/install.rb +9 -7
  14. data/lib/nesta/helpers.rb +14 -0
  15. data/lib/nesta/models.rb +26 -22
  16. data/lib/nesta/navigation.rb +1 -1
  17. data/lib/nesta/version.rb +1 -1
  18. data/nesta.gemspec +10 -11
  19. data/templates/config/config.yml +1 -1
  20. data/{spec → test}/fixtures/nesta-plugin-test/Gemfile +0 -0
  21. data/{spec → test}/fixtures/nesta-plugin-test/Rakefile +0 -0
  22. data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test.rb +0 -0
  23. data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test/init.rb +0 -0
  24. data/{spec → test}/fixtures/nesta-plugin-test/lib/nesta-plugin-test/version.rb +0 -0
  25. data/{spec → test}/fixtures/nesta-plugin-test/nesta-plugin-test.gemspec +0 -0
  26. data/test/integration/atom_feed_test.rb +178 -0
  27. data/test/integration/commands/demo/content_test.rb +31 -0
  28. data/test/integration/commands/edit_test.rb +21 -0
  29. data/test/integration/commands/new_test.rb +120 -0
  30. data/test/integration/commands/plugin/create_test.rb +128 -0
  31. data/test/integration/commands/theme/create_test.rb +35 -0
  32. data/test/integration/commands/theme/enable_test.rb +22 -0
  33. data/test/integration/commands/theme/install_test.rb +62 -0
  34. data/test/integration/default_theme_test.rb +220 -0
  35. data/test/integration/overrides_test.rb +118 -0
  36. data/test/integration/route_handlers_test.rb +96 -0
  37. data/test/integration/sitemap_test.rb +85 -0
  38. data/test/integration_test_helper.rb +61 -0
  39. data/test/support/model_factory.rb +169 -0
  40. data/test/support/silence_commands_during_tests.rb +5 -0
  41. data/test/support/temporary_files.rb +33 -0
  42. data/test/support/test_configuration.rb +19 -0
  43. data/test/test_helper.rb +26 -0
  44. data/test/unit/commands_test.rb +23 -0
  45. data/test/unit/config_test.rb +138 -0
  46. data/test/unit/file_model_test.rb +71 -0
  47. data/test/unit/menu_test.rb +82 -0
  48. data/test/unit/page_test.rb +571 -0
  49. data/test/unit/path_test.rb +41 -0
  50. data/test/unit/plugin_test.rb +47 -0
  51. data/views/master.sass +1 -1
  52. metadata +81 -85
  53. data/smoke-test.sh +0 -107
  54. data/spec/atom_spec.rb +0 -141
  55. data/spec/commands/demo/content_spec.rb +0 -65
  56. data/spec/commands/edit_spec.rb +0 -27
  57. data/spec/commands/new_spec.rb +0 -88
  58. data/spec/commands/plugin/create_spec.rb +0 -97
  59. data/spec/commands/system_spec.rb +0 -25
  60. data/spec/commands/theme/create_spec.rb +0 -41
  61. data/spec/commands/theme/enable_spec.rb +0 -44
  62. data/spec/commands/theme/install_spec.rb +0 -56
  63. data/spec/config_spec.rb +0 -127
  64. data/spec/model_factory.rb +0 -92
  65. data/spec/models_spec.rb +0 -700
  66. data/spec/overrides_spec.rb +0 -132
  67. data/spec/page_spec.rb +0 -560
  68. data/spec/path_spec.rb +0 -28
  69. data/spec/plugin_spec.rb +0 -51
  70. data/spec/sitemap_spec.rb +0 -105
  71. 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
@@ -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