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,96 @@
1
+ require 'integration_test_helper'
2
+
3
+ describe 'Routing' do
4
+ include Nesta::IntegrationTest
5
+
6
+ it 'redirects requests with trailing slash' do
7
+ with_temp_content_directory do
8
+ model = create(:page)
9
+ visit model.path + '/'
10
+ assert_equal model.abspath, page.current_path
11
+ end
12
+ end
13
+
14
+ describe 'not_found handler' do
15
+ it 'returns HTTP code 404' do
16
+ with_temp_content_directory do
17
+ visit '/no-such-page'
18
+ assert_equal 404, page.status_code
19
+ end
20
+ end
21
+ end
22
+
23
+ describe 'default route' do
24
+ it 'provides access to helper methods in Haml pages' do
25
+ with_temp_content_directory do
26
+ model = create(:category,
27
+ ext: 'haml',
28
+ content: '%div= format_date(Date.new(2010, 11, 23))')
29
+ visit model.path
30
+ assert_has_css 'div', text: '23 November 2010'
31
+ end
32
+ end
33
+
34
+ it "should access helpers when rendering articles on a category page" do
35
+ with_temp_content_directory do
36
+ category = create(:page)
37
+ markup = "%h1 Heading\n\n%div= format_date(Date.new(2010, 11, 23))"
38
+ article = create(:article,
39
+ ext: 'haml',
40
+ metadata: { 'categories' => category.path },
41
+ content: markup)
42
+ visit category.path
43
+ assert_has_css 'div', text: '23 November 2010'
44
+ end
45
+ end
46
+ end
47
+
48
+ def create_attachment_in(directory)
49
+ path = File.join(directory, 'test.txt')
50
+ FileUtils.mkdir_p(directory)
51
+ File.open(path, 'w') { |file| file.write("I'm a test attachment") }
52
+ end
53
+
54
+ describe 'attachments route' do
55
+ it 'returns HTTP code 200' do
56
+ with_temp_content_directory do
57
+ create_attachment_in(Nesta::Config.attachment_path)
58
+ visit '/attachments/test.txt'
59
+ assert_equal 200, page.status_code
60
+ end
61
+ end
62
+
63
+ it 'serves attachment to the client' do
64
+ with_temp_content_directory do
65
+ create_attachment_in(Nesta::Config.attachment_path)
66
+ visit '/attachments/test.txt'
67
+ assert_equal "I'm a test attachment", page.body
68
+ end
69
+ end
70
+
71
+ it 'sets the appropriate MIME type' do
72
+ with_temp_content_directory do
73
+ create_attachment_in(Nesta::Config.attachment_path)
74
+ visit '/attachments/test.txt'
75
+ assert_match %r{^text/plain}, page.response_headers['Content-Type']
76
+ end
77
+ end
78
+
79
+ it 'refuses to serve files outside the attachments directory' do
80
+ # On earlier versions of Sinatra this test would have been handed
81
+ # to the attachments handler. On the current version (1.4.5) it
82
+ # appears as though the request is no longer matched by the
83
+ # attachments route handler, which means we're not in danger of
84
+ # serving serving files to attackers via `#send_file`.
85
+ #
86
+ # I've left the test in anyway, as without it we wouldn't become
87
+ # aware of a security hole if there was a regression in Sinatra.
88
+ #
89
+ with_temp_content_directory do
90
+ model = create(:page)
91
+ visit "/attachments/../pages/#{File.basename(model.filename)}"
92
+ assert_equal 404, page.status_code
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,85 @@
1
+ require 'integration_test_helper'
2
+
3
+ describe 'XML sitemap' do
4
+ include Nesta::IntegrationTest
5
+
6
+ def visit_sitemap
7
+ visit '/sitemap.xml'
8
+ end
9
+
10
+ def for_site_with_page(&block)
11
+ with_temp_content_directory do
12
+ model = create(:page)
13
+ visit_sitemap
14
+ yield(model)
15
+ end
16
+ end
17
+
18
+ def for_site_with_article(&block)
19
+ with_temp_content_directory do
20
+ article = create(:article)
21
+ visit_sitemap
22
+ yield(article)
23
+ end
24
+ end
25
+
26
+ it 'renders successfully' do
27
+ for_site_with_page do
28
+ assert_equal 200, page.status_code
29
+ end
30
+ end
31
+
32
+ it 'has a urlset tag' do
33
+ for_site_with_page do
34
+ namespace = 'http://www.sitemaps.org/schemas/sitemap/0.9'
35
+ assert_has_xpath "//urlset[@xmlns='#{namespace}']"
36
+ end
37
+ end
38
+
39
+ it 'references the home page' do
40
+ for_site_with_page do
41
+ assert_has_xpath '//urlset/url/loc', text: 'http://www.example.com/'
42
+ end
43
+ end
44
+
45
+ it 'configures home page to be checked frequently' do
46
+ for_site_with_page do
47
+ assert_has_xpath '//urlset/url/loc', text: "http://www.example.com/"
48
+ assert_has_xpath '//urlset/url/changefreq', text: "daily"
49
+ assert_has_xpath '//urlset/url/priority', text: "1.0"
50
+ end
51
+ end
52
+
53
+ it "sets homepage lastmod from timestamp of most recently modified page" do
54
+ for_site_with_article do |article|
55
+ timestamp = article.last_modified
56
+ assert_has_xpath '//urlset/url/loc', text: "http://www.example.com/"
57
+ assert_has_xpath '//urlset/url/lastmod', text: timestamp.xmlschema
58
+ end
59
+ end
60
+
61
+ def site_url(path)
62
+ "http://www.example.com/#{path}"
63
+ end
64
+
65
+ it 'references category pages' do
66
+ for_site_with_page do |model|
67
+ assert_has_xpath '//urlset/url/loc', text: site_url(model.path)
68
+ end
69
+ end
70
+
71
+ it 'references article pages' do
72
+ for_site_with_article do |article|
73
+ assert_has_xpath '//urlset/url/loc', text: site_url(article.path)
74
+ end
75
+ end
76
+
77
+ it 'omits pages that have the skip-sitemap flag set' do
78
+ with_temp_content_directory do
79
+ create(:category)
80
+ omitted = create(:page, metadata: { 'flags' => 'skip-sitemap' })
81
+ visit_sitemap
82
+ assert_has_no_xpath '//urlset/url/loc', text: site_url(omitted.path)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,61 @@
1
+ require 'capybara/dsl'
2
+
3
+ require_relative 'test_helper'
4
+
5
+ module Nesta
6
+ class App < Sinatra::Base
7
+ set :environment, :test
8
+ set :reload_templates, true
9
+ end
10
+
11
+ module IntegrationTest
12
+ include Capybara::DSL
13
+
14
+ include ModelFactory
15
+ include TestConfiguration
16
+
17
+ def setup
18
+ Capybara.app = App.new
19
+ end
20
+
21
+ def teardown
22
+ Capybara.reset_sessions!
23
+ Capybara.use_default_driver
24
+
25
+ remove_temp_directory
26
+ Nesta::FileModel.purge_cache
27
+ end
28
+
29
+ def assert_has_xpath(query, options = {})
30
+ if ! page.has_xpath?(query, options)
31
+ message = "not found in page: '#{query}'"
32
+ message << ", #{options.inspect}" unless options.empty?
33
+ fail message
34
+ end
35
+ end
36
+
37
+ def assert_has_no_xpath(query, options = {})
38
+ if page.has_xpath?(query, options)
39
+ message = "found in page: '#{query}'"
40
+ message << ", #{options.inspect}" unless options.empty?
41
+ fail message
42
+ end
43
+ end
44
+
45
+ def assert_has_css(query, options = {})
46
+ if ! page.has_css?(query, options)
47
+ message = "not found in page: '#{query}'"
48
+ message << ", #{options.inspect}" unless options.empty?
49
+ fail message
50
+ end
51
+ end
52
+
53
+ def assert_has_no_css(query, options = {})
54
+ if ! page.has_no_css?(query, options)
55
+ message = "found in page: '#{query}'"
56
+ message << ", #{options.inspect}" unless options.empty?
57
+ fail message
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,169 @@
1
+ module ModelFactory
2
+ class FileModelWriter
3
+ def initialize(type, data = {})
4
+ @type = type
5
+ @data = data
6
+ end
7
+
8
+ def model_class
9
+ Nesta::FileModel
10
+ end
11
+
12
+ def instantiate_model
13
+ model_class.new(filename)
14
+ end
15
+
16
+ def extension
17
+ data.fetch(:ext, 'mdown')
18
+ end
19
+
20
+ def filename
21
+ File.join(model_class.model_path, "#{data[:path]}.#{extension}")
22
+ end
23
+
24
+ def sequence_prefix
25
+ 'file-'
26
+ end
27
+
28
+ def default_data
29
+ { path: default_path }
30
+ end
31
+
32
+ def default_path
33
+ sequence_prefix + ModelFactory.next_sequence_number.to_s
34
+ end
35
+
36
+ def data
37
+ if @memoized_data.nil?
38
+ @memoized_data = default_data
39
+ if @memoized_data.has_key?(:metadata)
40
+ @memoized_data[:metadata].merge!(@data.delete(:metadata) || {})
41
+ end
42
+ @memoized_data.merge!(@data)
43
+ end
44
+ @memoized_data
45
+ end
46
+
47
+ def write
48
+ metadata = data[:metadata] || {}
49
+ metatext = metadata.map { |key, value| "#{key}: #{value}" }.join("\n")
50
+ contents =<<-EOF
51
+ #{metatext}
52
+
53
+ #{heading}#{data[:content]}
54
+ EOF
55
+ FileUtils.mkdir_p(File.dirname(filename))
56
+ File.open(filename, 'w') { |file| file.write(contents) }
57
+ yield(filename) if block_given?
58
+ end
59
+
60
+ def heading
61
+ return '' unless data[:heading]
62
+ prefix = {
63
+ 'haml' => "%div\n %h1",
64
+ 'textile' => "<div>\nh1."
65
+ }.fetch(data[:ext], '# ')
66
+ "#{prefix} #{data[:heading]}\n\n"
67
+ end
68
+ end
69
+
70
+ class PageWriter < FileModelWriter
71
+ def model_class
72
+ Nesta::Page
73
+ end
74
+
75
+ def sequence_prefix
76
+ 'page-'
77
+ end
78
+
79
+ def default_data
80
+ path = default_path
81
+ { path: path, heading: heading_from_path(path) }
82
+ end
83
+
84
+ def heading_from_path(path)
85
+ File.basename(path).sub('-', ' ').capitalize
86
+ end
87
+ end
88
+
89
+ class ArticleWriter < PageWriter
90
+ def sequence_prefix
91
+ 'articles/page-'
92
+ end
93
+
94
+ def default_data
95
+ path = default_path
96
+ {
97
+ path: path,
98
+ heading: heading_from_path(path),
99
+ content: 'Content goes here',
100
+ metadata: {
101
+ 'date' => '29 December 2008'
102
+ }
103
+ }
104
+ end
105
+ end
106
+
107
+ class CategoryWriter < PageWriter
108
+ def sequence_prefix
109
+ 'categories/page-'
110
+ end
111
+
112
+ def default_data
113
+ path = default_path
114
+ {
115
+ path: path,
116
+ heading: heading_from_path(path),
117
+ content: 'Content goes here'
118
+ }
119
+ end
120
+ end
121
+
122
+ @@sequence = 0
123
+ def self.next_sequence_number
124
+ @@sequence += 1
125
+ end
126
+
127
+ def before_setup
128
+ # This is a minitest hook. We reset file sequence number at the
129
+ # start of each test so that we can automatically generate a unique
130
+ # path for each file we create within a test.
131
+ @@sequence = 0
132
+ end
133
+
134
+ def create(type, data = {}, &block)
135
+ file_writer = writer_class(type).new(type, data)
136
+ file_writer.write(&block)
137
+ file_writer.instantiate_model
138
+ end
139
+
140
+ def write_menu_item(indent, file, menu_item)
141
+ if menu_item.is_a?(Array)
142
+ indent.sub!(/^/, ' ')
143
+ menu_item.each { |path| write_menu_item(indent, file, path) }
144
+ indent.sub!(/^ /, '')
145
+ else
146
+ file.write("#{indent}#{menu_item}\n")
147
+ end
148
+ end
149
+
150
+ def create_menu(menu_text)
151
+ file = filename(Nesta::Config.content_path, 'menu', :txt)
152
+ File.open(file, 'w') { |file| file.write(menu_text) }
153
+ end
154
+
155
+ def create_content_directories
156
+ FileUtils.mkdir_p(Nesta::Config.page_path)
157
+ FileUtils.mkdir_p(Nesta::Config.attachment_path)
158
+ end
159
+
160
+ private
161
+ def filename(directory, basename, extension = :mdown)
162
+ File.join(directory, "#{basename}.#{extension}")
163
+ end
164
+
165
+ def writer_class(type)
166
+ camelcased_type = type.to_s.gsub(/(?:^|_)(\w)/) { $1.upcase }
167
+ ModelFactory.const_get(camelcased_type + 'Writer')
168
+ end
169
+ end
@@ -0,0 +1,5 @@
1
+ module SilenceCommandsDuringTests
2
+ def run_process(*args)
3
+ super(*args, out: '/dev/null', err: '/dev/null')
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module TemporaryFiles
2
+ TEMP_DIR = File.expand_path('tmp', File.join(File.dirname(__FILE__), '..'))
3
+
4
+ def remove_temp_directory
5
+ if File.exist?(TemporaryFiles::TEMP_DIR)
6
+ FileUtils.rm_r(TemporaryFiles::TEMP_DIR)
7
+ end
8
+ end
9
+
10
+ def temp_path(base)
11
+ File.join(TemporaryFiles::TEMP_DIR, base)
12
+ end
13
+
14
+ def project_root
15
+ temp_path('mysite.com')
16
+ end
17
+
18
+ def project_path(path)
19
+ File.join(project_root, path)
20
+ end
21
+
22
+ def in_temporary_project(*args, &block)
23
+ FileUtils.mkdir_p(File.join(project_root, 'config'))
24
+ File.open(File.join(project_root, 'config', 'config.yml'), 'w').close
25
+ Dir.chdir(project_root) { yield }
26
+ ensure
27
+ remove_temp_directory
28
+ end
29
+
30
+ def assert_exists_in_project(path)
31
+ assert File.exist?(project_path(path)), "#{path} should exist"
32
+ end
33
+ end