enki-engine 0.0.2

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