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.
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +284 -0
- data/README.textile +112 -0
- data/Rakefile +13 -0
- data/TODO.textile +8 -0
- data/app/assets/images/admin/flash_bg.gif +0 -0
- data/app/assets/images/admin/future_bg.png +0 -0
- data/app/assets/images/admin/gray_bg.gif +0 -0
- data/app/assets/images/admin/new_button.png +0 -0
- data/app/assets/images/admin/silver_bg.gif +0 -0
- data/app/assets/images/admin/submit_bg.gif +0 -0
- data/app/assets/images/admin/subnav_bg.gif +0 -0
- data/app/assets/images/openid_icon.png +0 -0
- data/app/assets/images/rails.png +0 -0
- data/app/assets/images/silk/arrow_undo.png +0 -0
- data/app/assets/images/silk/delete.png +0 -0
- data/app/assets/images/silk/pencil.png +0 -0
- data/app/assets/javascripts/admin/actions.js +18 -0
- data/app/assets/javascripts/admin/comments.js +3 -0
- data/app/assets/javascripts/admin/common.js +109 -0
- data/app/assets/javascripts/admin/dashboard.js +33 -0
- data/app/assets/javascripts/admin/edit-preview.js +40 -0
- data/app/assets/javascripts/admin/pages.js +1 -0
- data/app/assets/javascripts/admin/posts.js +1 -0
- data/app/assets/javascripts/admin/shortcut.js +223 -0
- data/app/assets/javascripts/admin.js +17 -0
- data/app/assets/javascripts/application.js +15 -0
- data/app/assets/javascripts/common.js +22 -0
- data/app/assets/javascripts/enki.js +13 -0
- data/app/assets/javascripts/live-comment-preview.js +36 -0
- data/app/assets/stylesheets/admin.css +304 -0
- data/app/assets/stylesheets/application.css.scss +486 -0
- data/app/assets/stylesheets/humanmsg.css +112 -0
- data/app/assets/stylesheets/login.css +65 -0
- data/app/controllers/enki/admin/base_controller.rb +15 -0
- data/app/controllers/enki/admin/comments_controller.rb +52 -0
- data/app/controllers/enki/admin/dashboard_controller.rb +12 -0
- data/app/controllers/enki/admin/health_controller.rb +20 -0
- data/app/controllers/enki/admin/pages_controller.rb +97 -0
- data/app/controllers/enki/admin/posts_controller.rb +104 -0
- data/app/controllers/enki/admin/undo_items_controller.rb +43 -0
- data/app/controllers/enki/application_controller.rb +21 -0
- data/app/controllers/enki/archives_controller.rb +7 -0
- data/app/controllers/enki/base_controller.rb +5 -0
- data/app/controllers/enki/comments_controller.rb +43 -0
- data/app/controllers/enki/pages_controller.rb +7 -0
- data/app/controllers/enki/posts_controller.rb +27 -0
- data/app/helpers/enki/admin/navigation_helper.rb +10 -0
- data/app/helpers/enki/application_helper.rb +35 -0
- data/app/helpers/enki/date_helper.rb +15 -0
- data/app/helpers/enki/form_helper.rb +7 -0
- data/app/helpers/enki/host_helper.rb +19 -0
- data/app/helpers/enki/navigation_helper.rb +23 -0
- data/app/helpers/enki/page_title_helper.rb +33 -0
- data/app/helpers/enki/posts_helper.rb +9 -0
- data/app/helpers/enki/tag_helper.rb +7 -0
- data/app/helpers/enki/url_helper.rb +25 -0
- data/app/models/enki/base/post.rb +153 -0
- data/app/models/enki/comment.rb +75 -0
- data/app/models/enki/comment_activity.rb +33 -0
- data/app/models/enki/delete_comment_undo.rb +33 -0
- data/app/models/enki/delete_page_undo.rb +32 -0
- data/app/models/enki/delete_post_undo.rb +36 -0
- data/app/models/enki/page.rb +42 -0
- data/app/models/enki/post.rb +4 -0
- data/app/models/enki/stats.rb +19 -0
- data/app/models/enki/tag.rb +19 -0
- data/app/models/enki/tagging.rb +7 -0
- data/app/models/enki/undo_item.rb +10 -0
- data/app/views/enki/admin/comments/_comment.html.erb +12 -0
- data/app/views/enki/admin/comments/index.html.erb +30 -0
- data/app/views/enki/admin/comments/show.html.erb +9 -0
- data/app/views/enki/admin/dashboard/show.html.erb +61 -0
- data/app/views/enki/admin/health/index.html.erb +3 -0
- data/app/views/enki/admin/pages/_form.html.erb +3 -0
- data/app/views/enki/admin/pages/_page.html.erb +12 -0
- data/app/views/enki/admin/pages/index.html.erb +25 -0
- data/app/views/enki/admin/pages/new.html.erb +8 -0
- data/app/views/enki/admin/pages/show.html.erb +8 -0
- data/app/views/enki/admin/posts/_form.html.erb +11 -0
- data/app/views/enki/admin/posts/_post.html.erb +12 -0
- data/app/views/enki/admin/posts/_taggings_form.html.erb +4 -0
- data/app/views/enki/admin/posts/_upload_form.html.erb +0 -0
- data/app/views/enki/admin/posts/index.html.erb +25 -0
- data/app/views/enki/admin/posts/new.html.erb +8 -0
- data/app/views/enki/admin/posts/show.html.erb +8 -0
- data/app/views/enki/admin/undo_items/index.html.erb +24 -0
- data/app/views/enki/archives/index.html.erb +17 -0
- data/app/views/enki/comments/_comment.html.erb +4 -0
- data/app/views/enki/pages/_page.html.erb +3 -0
- data/app/views/enki/pages/show.html.erb +5 -0
- data/app/views/enki/posts/_post.html.erb +13 -0
- data/app/views/enki/posts/index.atom.builder +27 -0
- data/app/views/enki/posts/index.html.erb +15 -0
- data/app/views/enki/posts/show.html.erb +37 -0
- data/autotest/discover.rb +2 -0
- data/config/cucumber.yml +8 -0
- data/config/enki.yml.sample +16 -0
- data/config/initializers/enki_ext.rb +3 -0
- data/config/initializers/set_chronic_timezone.rb +1 -0
- data/config/initializers/verification.rb +135 -0
- data/config/routes.rb +34 -0
- data/db/migrate/20110709024316_initialize_db.rb +97 -0
- data/db/seeds.rb +8 -0
- data/enki-engine.gemspec +47 -0
- data/features/admin_dashboard.feature +10 -0
- data/features/admin_health.feature +10 -0
- data/features/admin_undo.feature +20 -0
- data/features/browsing.feature +16 -0
- data/features/step_definitions/admin.rb +27 -0
- data/features/step_definitions/browsing.rb +3 -0
- data/features/step_definitions/posts.rb +11 -0
- data/features/step_definitions/web_steps.rb +1 -0
- data/features/support/env.rb +59 -0
- data/features/support/paths.rb +35 -0
- data/features/support/selectors.rb +39 -0
- data/lib/core_extensions/object.rb +9 -0
- data/lib/core_extensions/string.rb +22 -0
- data/lib/enki/config.rb +44 -0
- data/lib/enki/engine.rb +19 -0
- data/lib/enki/html5_tags.rb +8 -0
- data/lib/enki/pagination_shim.rb +25 -0
- data/lib/enki/version.rb +3 -0
- data/lib/enki.rb +14 -0
- data/lib/enki_formatter.rb +11 -0
- data/lib/tag_list.rb +2 -0
- data/lib/tags_helper.rb +13 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/enki.rake +29 -0
- data/lib/undo_failed.rb +2 -0
- data/script/cucumber +10 -0
- data/spec/controllers/admin/comments_controller_spec.rb +140 -0
- data/spec/controllers/admin/dashboard_controller_spec.rb +47 -0
- data/spec/controllers/admin/health_controller_spec.rb +49 -0
- data/spec/controllers/admin/pages_controller_spec.rb +136 -0
- data/spec/controllers/admin/posts_controller_spec.rb +183 -0
- data/spec/controllers/admin/undo_items_controller_spec.rb +93 -0
- data/spec/controllers/archives_controller_spec.rb +37 -0
- data/spec/controllers/comments_controller_spec.rb +126 -0
- data/spec/controllers/pages_controller_spec.rb +46 -0
- data/spec/controllers/posts_controller_spec.rb +168 -0
- data/spec/dummy/Gemfile +5 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/config/application.rb +34 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +6 -0
- data/spec/dummy/config/enki.yml +20 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +14 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/factories.rb +36 -0
- data/spec/helpers/page_title_helper_spec.rb +54 -0
- data/spec/helpers/url_helper_spec.rb +23 -0
- data/spec/lib/slugorize_spec.rb +44 -0
- data/spec/models/comment_activity_spec.rb +60 -0
- data/spec/models/comment_spec.rb +125 -0
- data/spec/models/delete_comment_undo_spec.rb +52 -0
- data/spec/models/delete_post_undo_spec.rb +18 -0
- data/spec/models/page_spec.rb +75 -0
- data/spec/models/post_spec.rb +257 -0
- data/spec/models/stats_spec.rb +28 -0
- data/spec/models/tag_spec.rb +13 -0
- data/spec/models/tagging_spec.rb +30 -0
- data/spec/rcov.opts +2 -0
- data/spec/routing/admin/pages_routing_spec.rb +29 -0
- data/spec/routing/archives_routing_spec.rb +9 -0
- data/spec/routing/comments_routing_spec.rb +17 -0
- data/spec/routing/pages_routing_spec.rb +9 -0
- data/spec/routing/posts_routing_spec.rb +26 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/be_valid_html5.rb +150 -0
- data/spec/support/be_valid_xhtml.rb +148 -0
- data/spec/support/routes_override_helper.rb +12 -0
- data/spec/views/admin/comments/index.html_spec.rb +26 -0
- data/spec/views/admin/comments/show.html_spec.rb +28 -0
- data/spec/views/admin/dashboard/show.html_spec.rb +37 -0
- data/spec/views/admin/pages/index.html_spec.rb +23 -0
- data/spec/views/admin/pages/new.html_spec.rb +16 -0
- data/spec/views/admin/pages/show.html_spec.rb +16 -0
- data/spec/views/admin/posts/index.html_spec.rb +24 -0
- data/spec/views/admin/posts/new.html_spec.rb +16 -0
- data/spec/views/admin/posts/show.html_spec.rb +16 -0
- data/spec/views/admin/undo_items/index.html_spec.rb +19 -0
- data/spec/views/archives/index.html_spec.rb +34 -0
- data/spec/views/pages/show.html_spec.rb +23 -0
- data/spec/views/posts/index.atom.builder_spec.rb +36 -0
- data/spec/views/posts/index.html_spec.rb +39 -0
- data/spec/views/posts/show.html_spec.rb +49 -0
- data/vendor/assets/javascripts/humanmsg.js +86 -0
- data/vendor/assets/javascripts/jquery.easing.1.3.js +205 -0
- data/vendor/assets/javascripts/jquery.form.js +869 -0
- data/vendor/assets/javascripts/jquery.jfeed.js +143 -0
- data/vendor/assets/javascripts/jquery.livequery.js +250 -0
- 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
data/spec/spec_helper.rb
ADDED
|
@@ -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,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
|