nesta 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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