enki-engine 0.0.2

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 (206) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +284 -0
  6. data/README.textile +112 -0
  7. data/Rakefile +13 -0
  8. data/TODO.textile +8 -0
  9. data/app/assets/images/admin/flash_bg.gif +0 -0
  10. data/app/assets/images/admin/future_bg.png +0 -0
  11. data/app/assets/images/admin/gray_bg.gif +0 -0
  12. data/app/assets/images/admin/new_button.png +0 -0
  13. data/app/assets/images/admin/silver_bg.gif +0 -0
  14. data/app/assets/images/admin/submit_bg.gif +0 -0
  15. data/app/assets/images/admin/subnav_bg.gif +0 -0
  16. data/app/assets/images/openid_icon.png +0 -0
  17. data/app/assets/images/rails.png +0 -0
  18. data/app/assets/images/silk/arrow_undo.png +0 -0
  19. data/app/assets/images/silk/delete.png +0 -0
  20. data/app/assets/images/silk/pencil.png +0 -0
  21. data/app/assets/javascripts/admin/actions.js +18 -0
  22. data/app/assets/javascripts/admin/comments.js +3 -0
  23. data/app/assets/javascripts/admin/common.js +109 -0
  24. data/app/assets/javascripts/admin/dashboard.js +33 -0
  25. data/app/assets/javascripts/admin/edit-preview.js +40 -0
  26. data/app/assets/javascripts/admin/pages.js +1 -0
  27. data/app/assets/javascripts/admin/posts.js +1 -0
  28. data/app/assets/javascripts/admin/shortcut.js +223 -0
  29. data/app/assets/javascripts/admin.js +17 -0
  30. data/app/assets/javascripts/application.js +15 -0
  31. data/app/assets/javascripts/common.js +22 -0
  32. data/app/assets/javascripts/enki.js +13 -0
  33. data/app/assets/javascripts/live-comment-preview.js +36 -0
  34. data/app/assets/stylesheets/admin.css +304 -0
  35. data/app/assets/stylesheets/application.css.scss +486 -0
  36. data/app/assets/stylesheets/humanmsg.css +112 -0
  37. data/app/assets/stylesheets/login.css +65 -0
  38. data/app/controllers/enki/admin/base_controller.rb +15 -0
  39. data/app/controllers/enki/admin/comments_controller.rb +52 -0
  40. data/app/controllers/enki/admin/dashboard_controller.rb +12 -0
  41. data/app/controllers/enki/admin/health_controller.rb +20 -0
  42. data/app/controllers/enki/admin/pages_controller.rb +97 -0
  43. data/app/controllers/enki/admin/posts_controller.rb +104 -0
  44. data/app/controllers/enki/admin/undo_items_controller.rb +43 -0
  45. data/app/controllers/enki/application_controller.rb +21 -0
  46. data/app/controllers/enki/archives_controller.rb +7 -0
  47. data/app/controllers/enki/base_controller.rb +5 -0
  48. data/app/controllers/enki/comments_controller.rb +43 -0
  49. data/app/controllers/enki/pages_controller.rb +7 -0
  50. data/app/controllers/enki/posts_controller.rb +27 -0
  51. data/app/helpers/enki/admin/navigation_helper.rb +10 -0
  52. data/app/helpers/enki/application_helper.rb +35 -0
  53. data/app/helpers/enki/date_helper.rb +15 -0
  54. data/app/helpers/enki/form_helper.rb +7 -0
  55. data/app/helpers/enki/host_helper.rb +19 -0
  56. data/app/helpers/enki/navigation_helper.rb +23 -0
  57. data/app/helpers/enki/page_title_helper.rb +33 -0
  58. data/app/helpers/enki/posts_helper.rb +9 -0
  59. data/app/helpers/enki/tag_helper.rb +7 -0
  60. data/app/helpers/enki/url_helper.rb +25 -0
  61. data/app/models/enki/base/post.rb +153 -0
  62. data/app/models/enki/comment.rb +75 -0
  63. data/app/models/enki/comment_activity.rb +33 -0
  64. data/app/models/enki/delete_comment_undo.rb +33 -0
  65. data/app/models/enki/delete_page_undo.rb +32 -0
  66. data/app/models/enki/delete_post_undo.rb +36 -0
  67. data/app/models/enki/page.rb +42 -0
  68. data/app/models/enki/post.rb +4 -0
  69. data/app/models/enki/stats.rb +19 -0
  70. data/app/models/enki/tag.rb +19 -0
  71. data/app/models/enki/tagging.rb +7 -0
  72. data/app/models/enki/undo_item.rb +10 -0
  73. data/app/views/enki/admin/comments/_comment.html.erb +12 -0
  74. data/app/views/enki/admin/comments/index.html.erb +30 -0
  75. data/app/views/enki/admin/comments/show.html.erb +9 -0
  76. data/app/views/enki/admin/dashboard/show.html.erb +61 -0
  77. data/app/views/enki/admin/health/index.html.erb +3 -0
  78. data/app/views/enki/admin/pages/_form.html.erb +3 -0
  79. data/app/views/enki/admin/pages/_page.html.erb +12 -0
  80. data/app/views/enki/admin/pages/index.html.erb +25 -0
  81. data/app/views/enki/admin/pages/new.html.erb +8 -0
  82. data/app/views/enki/admin/pages/show.html.erb +8 -0
  83. data/app/views/enki/admin/posts/_form.html.erb +11 -0
  84. data/app/views/enki/admin/posts/_post.html.erb +12 -0
  85. data/app/views/enki/admin/posts/_taggings_form.html.erb +4 -0
  86. data/app/views/enki/admin/posts/_upload_form.html.erb +0 -0
  87. data/app/views/enki/admin/posts/index.html.erb +25 -0
  88. data/app/views/enki/admin/posts/new.html.erb +8 -0
  89. data/app/views/enki/admin/posts/show.html.erb +8 -0
  90. data/app/views/enki/admin/undo_items/index.html.erb +24 -0
  91. data/app/views/enki/archives/index.html.erb +17 -0
  92. data/app/views/enki/comments/_comment.html.erb +4 -0
  93. data/app/views/enki/pages/_page.html.erb +3 -0
  94. data/app/views/enki/pages/show.html.erb +5 -0
  95. data/app/views/enki/posts/_post.html.erb +13 -0
  96. data/app/views/enki/posts/index.atom.builder +27 -0
  97. data/app/views/enki/posts/index.html.erb +15 -0
  98. data/app/views/enki/posts/show.html.erb +37 -0
  99. data/autotest/discover.rb +2 -0
  100. data/config/cucumber.yml +8 -0
  101. data/config/enki.yml.sample +16 -0
  102. data/config/initializers/enki_ext.rb +3 -0
  103. data/config/initializers/set_chronic_timezone.rb +1 -0
  104. data/config/initializers/verification.rb +135 -0
  105. data/config/routes.rb +34 -0
  106. data/db/migrate/20110709024316_initialize_db.rb +97 -0
  107. data/db/seeds.rb +8 -0
  108. data/enki-engine.gemspec +47 -0
  109. data/features/admin_dashboard.feature +10 -0
  110. data/features/admin_health.feature +10 -0
  111. data/features/admin_undo.feature +20 -0
  112. data/features/browsing.feature +16 -0
  113. data/features/step_definitions/admin.rb +27 -0
  114. data/features/step_definitions/browsing.rb +3 -0
  115. data/features/step_definitions/posts.rb +11 -0
  116. data/features/step_definitions/web_steps.rb +1 -0
  117. data/features/support/env.rb +59 -0
  118. data/features/support/paths.rb +35 -0
  119. data/features/support/selectors.rb +39 -0
  120. data/lib/core_extensions/object.rb +9 -0
  121. data/lib/core_extensions/string.rb +22 -0
  122. data/lib/enki/config.rb +44 -0
  123. data/lib/enki/engine.rb +19 -0
  124. data/lib/enki/html5_tags.rb +8 -0
  125. data/lib/enki/pagination_shim.rb +25 -0
  126. data/lib/enki/version.rb +3 -0
  127. data/lib/enki.rb +14 -0
  128. data/lib/enki_formatter.rb +11 -0
  129. data/lib/tag_list.rb +2 -0
  130. data/lib/tags_helper.rb +13 -0
  131. data/lib/tasks/cucumber.rake +65 -0
  132. data/lib/tasks/enki.rake +29 -0
  133. data/lib/undo_failed.rb +2 -0
  134. data/script/cucumber +10 -0
  135. data/spec/controllers/admin/comments_controller_spec.rb +140 -0
  136. data/spec/controllers/admin/dashboard_controller_spec.rb +47 -0
  137. data/spec/controllers/admin/health_controller_spec.rb +49 -0
  138. data/spec/controllers/admin/pages_controller_spec.rb +136 -0
  139. data/spec/controllers/admin/posts_controller_spec.rb +183 -0
  140. data/spec/controllers/admin/undo_items_controller_spec.rb +93 -0
  141. data/spec/controllers/archives_controller_spec.rb +37 -0
  142. data/spec/controllers/comments_controller_spec.rb +126 -0
  143. data/spec/controllers/pages_controller_spec.rb +46 -0
  144. data/spec/controllers/posts_controller_spec.rb +168 -0
  145. data/spec/dummy/Gemfile +5 -0
  146. data/spec/dummy/Rakefile +7 -0
  147. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  148. data/spec/dummy/config/application.rb +34 -0
  149. data/spec/dummy/config/boot.rb +10 -0
  150. data/spec/dummy/config/database.yml +6 -0
  151. data/spec/dummy/config/enki.yml +20 -0
  152. data/spec/dummy/config/environment.rb +5 -0
  153. data/spec/dummy/config/environments/development.rb +34 -0
  154. data/spec/dummy/config/environments/test.rb +32 -0
  155. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  156. data/spec/dummy/config/initializers/session_store.rb +8 -0
  157. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  158. data/spec/dummy/config/locales/en.yml +5 -0
  159. data/spec/dummy/config/routes.rb +14 -0
  160. data/spec/dummy/config.ru +6 -0
  161. data/spec/dummy/script/rails +6 -0
  162. data/spec/factories/factories.rb +36 -0
  163. data/spec/helpers/page_title_helper_spec.rb +54 -0
  164. data/spec/helpers/url_helper_spec.rb +23 -0
  165. data/spec/lib/slugorize_spec.rb +44 -0
  166. data/spec/models/comment_activity_spec.rb +60 -0
  167. data/spec/models/comment_spec.rb +125 -0
  168. data/spec/models/delete_comment_undo_spec.rb +52 -0
  169. data/spec/models/delete_post_undo_spec.rb +18 -0
  170. data/spec/models/page_spec.rb +75 -0
  171. data/spec/models/post_spec.rb +257 -0
  172. data/spec/models/stats_spec.rb +28 -0
  173. data/spec/models/tag_spec.rb +13 -0
  174. data/spec/models/tagging_spec.rb +30 -0
  175. data/spec/rcov.opts +2 -0
  176. data/spec/routing/admin/pages_routing_spec.rb +29 -0
  177. data/spec/routing/archives_routing_spec.rb +9 -0
  178. data/spec/routing/comments_routing_spec.rb +17 -0
  179. data/spec/routing/pages_routing_spec.rb +9 -0
  180. data/spec/routing/posts_routing_spec.rb +26 -0
  181. data/spec/spec.opts +4 -0
  182. data/spec/spec_helper.rb +60 -0
  183. data/spec/support/be_valid_html5.rb +150 -0
  184. data/spec/support/be_valid_xhtml.rb +148 -0
  185. data/spec/support/routes_override_helper.rb +12 -0
  186. data/spec/views/admin/comments/index.html_spec.rb +26 -0
  187. data/spec/views/admin/comments/show.html_spec.rb +28 -0
  188. data/spec/views/admin/dashboard/show.html_spec.rb +37 -0
  189. data/spec/views/admin/pages/index.html_spec.rb +23 -0
  190. data/spec/views/admin/pages/new.html_spec.rb +16 -0
  191. data/spec/views/admin/pages/show.html_spec.rb +16 -0
  192. data/spec/views/admin/posts/index.html_spec.rb +24 -0
  193. data/spec/views/admin/posts/new.html_spec.rb +16 -0
  194. data/spec/views/admin/posts/show.html_spec.rb +16 -0
  195. data/spec/views/admin/undo_items/index.html_spec.rb +19 -0
  196. data/spec/views/archives/index.html_spec.rb +34 -0
  197. data/spec/views/pages/show.html_spec.rb +23 -0
  198. data/spec/views/posts/index.atom.builder_spec.rb +36 -0
  199. data/spec/views/posts/index.html_spec.rb +39 -0
  200. data/spec/views/posts/show.html_spec.rb +49 -0
  201. data/vendor/assets/javascripts/humanmsg.js +86 -0
  202. data/vendor/assets/javascripts/jquery.easing.1.3.js +205 -0
  203. data/vendor/assets/javascripts/jquery.form.js +869 -0
  204. data/vendor/assets/javascripts/jquery.jfeed.js +143 -0
  205. data/vendor/assets/javascripts/jquery.livequery.js +250 -0
  206. metadata +464 -0
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'PostsController routes' do
4
+
5
+ it "should route /code to posts#index with tag code" do
6
+ get("/code").should route_to("enki/posts#index", :tag => 'code')
7
+ end
8
+
9
+ it "should route /posts.atmo to posts#index format atom" do
10
+ get("/posts.atom").should route_to("enki/posts#index", :format => 'atom')
11
+ end
12
+
13
+ it "should route /pages to posts#index with tag pages" do
14
+ get("/pages").should route_to('enki/posts#index', :tag => 'pages')
15
+ end
16
+
17
+ it "should route /code.atmo to posts#index tag: code and format: atom" do
18
+ get("/code.atom").should route_to("enki/posts#index", :tag => "code", :format => 'atom')
19
+ end
20
+
21
+ it "should route /2008/02/01/a-post to posts#show with year: 2008, month: 02, day: 01, slug: a-post" do
22
+ get("/2008/02/01/a-post").should route_to("enki/posts#show", :year => '2008', :month => '02', :day => '01', :slug => 'a-post')
23
+ end
24
+
25
+ end
26
+
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,60 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+
4
+ # See http://reinteractive.net/posts/2-start-your-engines for useful info regarding testing Engines.
5
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
6
+ require 'rspec/rails'
7
+ require 'factory_girl_rails'
8
+
9
+ # Add engine url_helpers to the base test app as we are testing the
10
+ # engine works not whether rails routing proxies work.
11
+ ::ApplicationController.send :include, Enki::Engine.routes.url_helpers
12
+
13
+ ENGINE_RAILS_ROOT = File.expand_path('../../', __FILE__)
14
+
15
+ # Requires supporting ruby files with custom matchers and macros, etc,
16
+ # in spec/support/ and its subdirectories.
17
+ Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each {|f| require f }
18
+
19
+ RSpec.configure do |config|
20
+ require 'rspec/expectations'
21
+
22
+ config.include RSpec::Matchers
23
+ # == Mock Framework
24
+ #
25
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
26
+ #
27
+ # config.mock_with :mocha
28
+ # config.mock_with :flexmock
29
+ # config.mock_with :rr
30
+ config.mock_with :rspec
31
+
32
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
33
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
34
+
35
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
36
+ # examples within a transaction, remove the following line or assign false
37
+ # instead of true.
38
+ config.use_transactional_fixtures = true
39
+
40
+ config.include FactoryGirl::Syntax::Methods
41
+
42
+ # Ensure the routes used by Rspec are the engine ones
43
+ config.include RoutesOverrideHelper
44
+ # Make the engines route helpers available to Rspec.
45
+ config.include Enki::Engine.routes.url_helpers
46
+
47
+ config.before(:all) do
48
+ ActiveRecord::Migration.verbose = false
49
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
50
+ ActiveRecord::Migrator.up([File.expand_path('../../db/migrate', __FILE__)]) { |migration| true }
51
+ end
52
+
53
+ config.render_views
54
+
55
+ end
56
+
57
+ module DisableFlashSweeping
58
+ def sweep
59
+ end
60
+ end
@@ -0,0 +1,150 @@
1
+ # Paste me into spec_helper.rb, or save me somewhere else and require me in.
2
+ require 'net/http'
3
+ require 'digest/md5'
4
+
5
+ class BeValidHtml5
6
+
7
+ def initialize(base, options)
8
+ @base = base
9
+ @fragment = options[:fragment]
10
+ end
11
+
12
+ # Assert that markup is valid according the W3C validator web service.
13
+ # By default, it validates the contents of @response.body, which is set after calling
14
+ # one of the get/post/etc helper methods. You can also pass it a string to be validated.
15
+ # Validation errors, if any, will be included in the output. The input fragment and
16
+ # response from the validator service will be cached in the $RAILS_ROOT/tmp directory to
17
+ # minimize network calls.
18
+ #
19
+ # For example, if you have a FooController with an action Bar, put this in foo_controller_test.rb:
20
+ #
21
+ # def test_bar_valid_markup
22
+ # get :bar
23
+ # assert_valid_markup
24
+ # end
25
+ #
26
+ MARKUP_VALIDATOR_HOST = ENV['MARKUP_VALIDATOR_HOST'] || 'validator.w3.org'
27
+ MARKUP_VALIDATOR_PATH = ENV['MARKUP_VALIDATOR_PATH'] || '/check'
28
+ CSS_VALIDATOR_HOST = ENV['CSS_VALIDATOR_HOST'] || 'jigsaw.w3.org'
29
+ CSS_VALIDATOR_PATH = ENV['CSS_VALIDATOR_PATH'] || '/css-validator/validator'
30
+
31
+ @@display_invalid_content = false
32
+ cattr_accessor :display_invalid_content
33
+
34
+ @@auto_validate = false
35
+ cattr_accessor :auto_validate
36
+
37
+ class_attribute :auto_validate_excludes
38
+ class_attribute :auto_validate_includes
39
+
40
+
41
+ def matches?(rendered)
42
+ fn = @base
43
+ fragment = rendered
44
+ fragment = wrap_with_html5_header(fragment) if @fragment
45
+ return true if validity_checks_disabled?
46
+ base_filename = cache_resource('markup',fragment,fn)
47
+
48
+ return false unless base_filename
49
+ results_filename = base_filename + '-results.yml'
50
+
51
+ begin
52
+ response = File.open(results_filename) do |f| Marshal.load(f) end
53
+ rescue
54
+ response = http.start(MARKUP_VALIDATOR_HOST).post2(MARKUP_VALIDATOR_PATH, "fragment=#{CGI.escape(fragment)}&output=xml")
55
+ File.open(results_filename, 'w+') do |f| Marshal.dump(response, f) end
56
+ end
57
+ markup_is_valid = response['x-w3c-validator-status'] == 'Valid'
58
+ unless markup_is_valid
59
+ fragment.split($/).each_with_index{|line, index| message << "#{'%04i' % (index+1)} : #{line}#{$/}"} if @@display_invalid_content
60
+ @message = "Invalid markup:\n"
61
+ @elements = Nokogiri::HTML(response.body.force_encoding('utf-8')).css("li.msg_err > span.msg")
62
+ (@elements).each { |span| @message << CGI.unescapeHTML(span.inner_html) + "\n" }
63
+ end
64
+ if markup_is_valid
65
+ return true
66
+ else
67
+ return false
68
+ end
69
+ end
70
+
71
+ def wrap_with_html5_header(fragment)
72
+ ret = <<-EOS
73
+ <!DOCTYPE html>
74
+ <html dir="ltr" lang="en-US">
75
+ <head>
76
+ <meta charset="utf-8">
77
+ <title>Test</title>
78
+ </head>
79
+ <body>
80
+ #{fragment}
81
+ </body>
82
+ </html>
83
+ EOS
84
+ end
85
+
86
+ def description
87
+ "be valid html5"
88
+ end
89
+
90
+ def failure_message
91
+ " expected html5 to be valid, but validation produced these errors:\n #{@message}"
92
+ end
93
+
94
+ def negative_failure_message
95
+ " expected to not be valid, but was (missing validation?)"
96
+ end
97
+
98
+ private
99
+ def validity_checks_disabled?
100
+ ENV["NET"] != 'true'
101
+ end
102
+
103
+ def text_to_multipart(key,value)
104
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n\r\n#{value}\r\n"
105
+ end
106
+
107
+ def file_to_multipart(key,filename,mime_type,content)
108
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"; filename=\"#{filename}\"\r\n" +
109
+ "Content-Transfer-Encoding: binary\r\nContent-Type: #{mime_type}\r\n\r\n#{content}\r\n"
110
+ end
111
+
112
+ def cache_resource(base,resource,fn)
113
+ resource_md5 = Digest::MD5.hexdigest(resource).to_s
114
+ file_md5 = nil
115
+
116
+ output_dir = "#{Rails.root}/tmp/#{base}"
117
+ base_filename = File.join(output_dir, fn)
118
+ filename = base_filename
119
+
120
+ parent_dir = File.dirname(filename)
121
+ FileUtils.mkdir_p(parent_dir) unless File.exists?(parent_dir)
122
+
123
+ File.open(filename, 'r') do |f|
124
+ file_md5 = Digest::MD5.hexdigest(f.read(f.stat.size)).to_s
125
+ end if File.exists?(filename)
126
+
127
+ if file_md5 != resource_md5
128
+ Dir["#{base_filename}[^.]*"] .each {|f| File.delete(f)}
129
+ File.open(filename, 'w+') do |f| f.write(resource); end
130
+ end
131
+ base_filename
132
+ end
133
+
134
+ def http
135
+ if Module.constants.include?("ApplicationConfig") && ApplicationConfig.respond_to?(:proxy_config)
136
+ Net::HTTP::Proxy(ApplicationConfig.proxy_config['host'], ApplicationConfig.proxy_config['port'])
137
+ else
138
+ Net::HTTP
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ def be_valid_html5
145
+ BeValidhtml5.new(subject)
146
+ end
147
+
148
+ def be_valid_html5_fragment
149
+ BeValidHtml5.new(subject, :fragment => true)
150
+ end
@@ -0,0 +1,148 @@
1
+ # Paste me into spec_helper.rb, or save me somewhere else and require me in.
2
+
3
+ class BeValidXhtml
4
+ require 'net/http'
5
+ require 'digest/md5'
6
+
7
+ def initialize(options)
8
+ @fragment = options[:fragment]
9
+ end
10
+
11
+ # Assert that markup (html/xhtml) is valid according the W3C validator web service.
12
+ # Validation errors, if any, will be included in the output. The input fragment and
13
+ # response from the validator service will be cached in the #{Rails.root}/tmp directory to
14
+ # minimize network calls.
15
+ #
16
+ # For example, if you have a FooController with an action Bar, put this in foo_controller_test.rb:
17
+ #
18
+ # def test_bar_valid_markup
19
+ # get :bar
20
+ # assert_valid_markup
21
+ # end
22
+ #
23
+ MARKUP_VALIDATOR_HOST = ENV['MARKUP_VALIDATOR_HOST'] || 'validator.w3.org'
24
+ MARKUP_VALIDATOR_PATH = ENV['MARKUP_VALIDATOR_PATH'] || '/check'
25
+ CSS_VALIDATOR_HOST = ENV['CSS_VALIDATOR_HOST'] || 'jigsaw.w3.org'
26
+ CSS_VALIDATOR_PATH = ENV['CSS_VALIDATOR_PATH'] || '/css-validator/validator'
27
+
28
+ @@display_invalid_content = false
29
+ cattr_accessor :display_invalid_content
30
+
31
+ @@auto_validate = false
32
+ cattr_accessor :auto_validate
33
+
34
+ class_attribute :auto_validate_excludes
35
+ class_attribute :auto_validate_includes
36
+
37
+
38
+ def matches?(rendered)
39
+ fragment = rendered
40
+ fragment = wrap_with_xhtml_header(fragment) if @fragment
41
+ return true if validity_checks_disabled?
42
+ base_filename = cache_resource('markup',fragment,'html')
43
+
44
+ return false unless base_filename
45
+ results_filename = base_filename + '-results.yml'
46
+
47
+ begin
48
+ response = File.open(results_filename) do |f| Marshal.load(f) end
49
+ rescue
50
+ response = http.start(MARKUP_VALIDATOR_HOST).post2(MARKUP_VALIDATOR_PATH, "fragment=#{CGI.escape(fragment)}&output=xml")
51
+ File.open(results_filename, 'w+') do |f| Marshal.dump(response, f) end
52
+ end
53
+ markup_is_valid = response['x-w3c-validator-status'] == 'Valid'
54
+ @message = ''
55
+ unless markup_is_valid
56
+ fragment.split($/).each_with_index{|line, index| message << "#{'%04i' % (index+1)} : #{line}#{$/}"} if @@display_invalid_content
57
+ @message << XmlSimple.xml_in(response.body)['messages'][0]['msg'].collect{ |m| "Invalid markup: line #{m['line']}: #{CGI.unescapeHTML(m['content'])}" }.join("\n")
58
+ end
59
+ if markup_is_valid
60
+ return true
61
+ else
62
+ return false
63
+ end
64
+ end
65
+
66
+ def wrap_with_xhtml_header(fragment)
67
+ ret = <<-EOS
68
+ <?xml version="1.0" encoding="UTF-8"?>
69
+ <!DOCTYPE html PUBLIC
70
+ "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
71
+ "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd">
72
+
73
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
74
+ <head>
75
+ <title>Test</title>
76
+ </head>
77
+ <body>
78
+ #{fragment}
79
+ </body>
80
+ </html>
81
+ EOS
82
+ end
83
+
84
+ def description
85
+ "be valid xhtml"
86
+ end
87
+
88
+ def failure_message
89
+ " expected xhtml to be valid, but validation produced these errors:\n #{@message}"
90
+ end
91
+
92
+ def negative_failure_message
93
+ " expected to not be valid, but was (missing validation?)"
94
+ end
95
+
96
+ private
97
+ def validity_checks_disabled?
98
+ ENV["NET"] != 'true'
99
+ end
100
+
101
+ def text_to_multipart(key,value)
102
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n\r\n#{value}\r\n"
103
+ end
104
+
105
+ def file_to_multipart(key,filename,mime_type,content)
106
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"; filename=\"#{filename}\"\r\n" +
107
+ "Content-Transfer-Encoding: binary\r\nContent-Type: #{mime_type}\r\n\r\n#{content}\r\n"
108
+ end
109
+
110
+ def cache_resource(base,rendered,extension)
111
+ rendered_md5 = Digest::MD5.hexdigest(rendered).to_s
112
+ file_md5 = nil
113
+
114
+ output_dir = "#{Rails.root}/tmp/#{base}"
115
+ base_filename = File.join(output_dir, rendered_md5)
116
+ filename = base_filename + '.' + extension
117
+
118
+ parent_dir = File.dirname(filename)
119
+ FileUtils.mkdir_p(parent_dir) unless File.exists?(parent_dir)
120
+
121
+ File.open(filename, 'r') do |f|
122
+ file_md5 = Digest::MD5.hexdigest(f.read(f.stat.size)).to_s
123
+ end if File.exists?(filename)
124
+
125
+ if file_md5 != rendered_md5
126
+ Dir["#{base_filename}[^.]*"] .each {|f| File.delete(f)}
127
+ File.open(filename, 'w+') do |f| f.write(rendered); end
128
+ end
129
+ base_filename
130
+ end
131
+
132
+ def http
133
+ if Module.constants.include?("ApplicationConfig") && ApplicationConfig.respond_to?(:proxy_config)
134
+ Net::HTTP::Proxy(ApplicationConfig.proxy_config['host'], ApplicationConfig.proxy_config['port'])
135
+ else
136
+ Net::HTTP
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ def be_valid_xhtml
143
+ BeValidXhtml.new
144
+ end
145
+
146
+ def be_valid_xhtml_fragment
147
+ BeValidXhtml.new(:fragment => true)
148
+ end
@@ -0,0 +1,12 @@
1
+ module RoutesOverrideHelper
2
+
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before do
7
+ @routes = Enki::Engine.routes
8
+ end
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/comments/index.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ comments = [mock_model(Comment,
12
+ :author => 'Don Alias',
13
+ :body => 'Hello I am a post',
14
+ :created_at => Time.now,
15
+ :post_title => 'A Post',
16
+ :post => mock_model(Post,
17
+ :slug => 'a-post',
18
+ :published_at => Time.now
19
+ )
20
+ )].extend PaginationShim
21
+ assign :comments, comments
22
+ render :template => '/enki/admin/comments/index', :formats => [:html]
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/comments/show.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :comment, mock_model(Comment,
12
+ :author => 'Don Alias',
13
+ :author_url => 'http://enkiblog.com',
14
+ :author_email => 'donalias@enkiblog.com',
15
+ :body => 'Hello I am a post',
16
+ :created_at => Time.now
17
+ )
18
+ allow_message_expectations_on_nil
19
+ assigns[:comment].stub!(:post).and_return(mock_model(Post,
20
+ :title => 'A post',
21
+ :slug => 'a-post',
22
+ :published_at => Time.now
23
+ ))
24
+ render :template => '/enki/admin/comments/show', :formats => [:html]
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/dashboard/show.html" do
6
+ before(:each) do
7
+ view.stub!(:enki_config).and_return(Enki::Config.default)
8
+ end
9
+
10
+ after(:each) do
11
+ rendered.should be_valid_html5_fragment
12
+ end
13
+
14
+ it 'should render' do
15
+ assign :posts, [mock_model(Post,
16
+ :title => 'A Post',
17
+ :published_at => Time.now,
18
+ :slug => 'a-post',
19
+ :approved_comments => []
20
+ )]
21
+ assign :pages, [create(:page)]
22
+ assign :comment_activity, [mock("comment-activity-1",
23
+ :post => mock_model(Post,
24
+ :published_at => Time.now,
25
+ :title => "A Post",
26
+ :slug => 'a-post',
27
+ :approved_comments => []
28
+ ),
29
+ :comments => [mock_model(Comment, :author => 'Don', :body_html => 'Hello')],
30
+ :most_recent_comment => mock_model(Comment, :created_at => Time.now, :author => 'Don')
31
+ )]
32
+ assign :stats, Struct.new(:post_count, :page_count, :comment_count, :tag_count).new(3,4,2,1)
33
+ render :template => '/enki/admin/dashboard/show', :formats => [:html]
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/pages/index.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ pages = [mock_model(Page,
12
+ :title => 'A page',
13
+ :body => 'Hello I am a page',
14
+ :slug => 'a-page',
15
+ :created_at => Time.now
16
+ )]
17
+ pages.stub!(:total_pages).and_return(1)
18
+ assign :pages, pages
19
+ render :template => '/enki/admin/pages/index', :formats => [:html]
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/pages/new.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :page, Page.new
12
+ render :template => '/enki/admin/pages/new', :formats => [:html]
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/pages/show.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :page, create(:page)
12
+ render :template => '/enki/admin/pages/show', :formats => [:html]
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/posts/index.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ posts = [mock_model(Post,
12
+ :published_at => Time.now,
13
+ :title => 'A post',
14
+ :body => 'Hello I am a post',
15
+ :slug => 'a-post',
16
+ :approved_comments => []
17
+ )]
18
+ posts.stub!(:total_pages).and_return(1)
19
+ assign :posts, posts
20
+ render :template => '/enki/admin/posts/index', :formats => [:html]
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/posts/new.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :post, Post.new
12
+ render :template => '/enki/admin/posts/new', :formats => [:html]
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/posts/show.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :post, create(:post)
12
+ render :template => '/enki/admin/posts/show', :formats => [:html]
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/admin/undo_items/index.html" do
6
+ after(:each) do
7
+ rendered.should be_valid_html5_fragment
8
+ end
9
+
10
+ it 'should render' do
11
+ assign :undo_items, [mock_model(UndoItem,
12
+ :created_at => Time.now,
13
+ :description => 'Deleted a comment'
14
+ )]
15
+ render :template => '/enki/admin/undo_items/index', :formats => [:html]
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module Enki
4
+
5
+ describe "/archives/index.html" do
6
+ def tag(name)
7
+ mock_model(Tag, :name => name)
8
+ end
9
+
10
+ before(:each) do
11
+ view.stub!(:enki_config).and_return(Enki::Config.default)
12
+
13
+ month = Struct.new(:date, :posts)
14
+ assign :months, [
15
+ month.new(1.month.ago.utc.beginning_of_month, [
16
+ mock_model(Post, :title => 'Post A', :published_at => 3.weeks.ago.utc, :slug => 'post-a', :tags => [tag("Code")])
17
+ ]),
18
+ month.new(2.months.ago.utc.beginning_of_month, [
19
+ mock_model(Post, :title => 'Post B', :published_at => 6.weeks.ago.utc, :slug => 'post-b', :tags => [tag("Code"), tag("Ruby")]),
20
+ mock_model(Post, :title => 'Post C', :published_at => 7.weeks.ago.utc, :slug => 'post-c', :tags => [])
21
+ ])
22
+ ]
23
+ end
24
+
25
+ after(:each) do
26
+ rendered.should be_valid_html5_fragment
27
+ end
28
+
29
+ it 'renders posts grouped by month' do
30
+ render :template => "/enki/archives/index", :formats => [:html]
31
+ end
32
+ end
33
+
34
+ end