Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: calagator/calagator
...
head fork: calagator/calagator
  • 18 commits
  • 28 files changed
  • 0 commit comments
  • 1 contributor
Commits on Sep 01, 2012
Igal Koshevoy igal Improve spec_helper to clean backtraces so they contain just relevant…
… information. Yay!
2112821
Igal Koshevoy igal Add "database_cleaner". Many specs are now broken because this expose…
…d dependency bugs.
8e2d6d1
Igal Koshevoy igal Fix VersionsController#edit to display deleted records, rather than f…
…ail with exceptions.
47bd700
Igal Koshevoy igal Fix Event duplicate checking to ignore :venue_id. 9c5a92e
Igal Koshevoy igal Change Event::time_for to raise exceptions on parsing errors. Refacto…
…r and document.
5fefd9a
Igal Koshevoy igal Fix events#index.atom to not raise exceptions if there are no events. 4ed4434
Igal Koshevoy igal MIGRATION: Fix venues latitude/longitude on MySQL.
Before, the schema used a default "decimal" column for these. Unfortunately,
MySQL interprets this as 10 digits, decimal and NO digits, which is crazy.

Now, the schema is 3 digits, decimal and 4 digits.
3167001
Igal Koshevoy igal Remove MySQL warning now that the last known bug with it has been res…
…olved.
10eb28b
Igal Koshevoy igal Improve MySQL sample database.yml to also read from DBUSER_MYSQL. 1631e0d
Igal Koshevoy igal Improve PostgreSQL sample database.yml to also read from DBUSER_POSTG…
…RESQL.
5448324
Igal Koshevoy igal Improve :event_without_venue factory to derive :end_time from :start_…
…time.
2ddd76e
Igal Koshevoy igal Fix :venue factory use sensible latitude/longitude. Add more fields. b078c1e
Igal Koshevoy igal Fix search by tag feed/subscription links. Extract them into helpers. 608e8c0
Igal Koshevoy igal Improve spec_helper_extensions::save_body to display file saved. 2502ad7
Igal Koshevoy igal Fix, refactor and improve specs: dependent examples, nesting, fixture…
…s, factories, etc.

* Fixed naughty dependent examples that "database_cleaner" exposed.
* Fixed poorly designed specs by properly nesting and naming them.
* Removed all uses of fixtures.
* Replaced many calls with factories.
* Rewrote confusing examples.
* Added missing examples.
7afe366
Igal Koshevoy igal Add `rake spec:db:all` and others to run tests against all or specifi…
…c databases.
40d8ed6
Igal Koshevoy igal Fix #updated_at, which may have been breaking iCal refreshes.
Some iCalendar clients rely on the timestamps provided by the #updated_at
colum, which wasn't being updated, so they assumed the record hadn't
changed despite the SEQUENCE number and content changing.

The problem is that Rails 3 does not update the #updated_at column on
#save, only on #create and #update_attributes. That's really stupid.

This change reimplements the old behavior of updating the #updated_at
column on #save as a mixin.
6a2a035
Igal Koshevoy igal Merge branch 'with_fixes': specify precision for latitude/longitude, …
…update #updated_at, search by tag links, ATOM feed without events, etc.

* with_fixes:
  Fix #updated_at, which may have been breaking iCal refreshes.
  Add `rake spec:db:all` and others to run tests against all or specific databases.
  Fix, refactor and improve specs: dependent examples, nesting, fixtures, factories, etc.
  Improve spec_helper_extensions::save_body to display file saved.
  Fix search by tag feed/subscription links. Extract them into helpers.
  Fix :venue factory use sensible latitude/longitude. Add more fields.
  Improve :event_without_venue factory to derive :end_time from :start_time.
  Improve PostgreSQL sample database.yml to also read from DBUSER_POSTGRESQL.
  Improve MySQL sample database.yml to also read from DBUSER_MYSQL.
  Remove MySQL warning now that the last known bug with it has been resolved.
  MIGRATION: Fix venues latitude/longitude on MySQL.
  Fix events#index.atom to not raise exceptions if there are no events.
  Change Event::time_for to raise exceptions on parsing errors. Refactor and document.
  Fix Event duplicate checking to ignore :venue_id.
  Fix VersionsController#edit to display deleted records, rather than fail with exceptions.
  Add "database_cleaner". Many specs are now broken because this exposed dependency bugs.
  Improve spec_helper to clean backtraces so they contain just relevant information. Yay!
4d36445
Showing with 1,167 additions and 1,012 deletions.
  1. +1 −0  Gemfile
  2. +1 −1  app/controllers/versions_controller.rb
  3. +59 −1 app/helpers/events_helper.rb
  4. +27 −16 app/models/event.rb
  5. +6 −0 app/models/update_updated_at_mixin.rb
  6. +2 −0  app/models/venue.rb
  7. +1 −1  app/views/events/index.atom.builder
  8. +4 −5 app/views/events/index.html.erb
  9. +5 −6 app/views/events/search.html.erb
  10. +2 −3 config/database~mysql.sample.yml
  11. +1 −1  config/database~postgresql.sample.yml
  12. +15 −0 db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb
  13. +55 −0 lib/tasks/spec_db.rake
  14. +652 −536 spec/controllers/events_controller_spec.rb
  15. +41 −43 spec/controllers/venues_controller_spec.rb
  16. +43 −54 spec/controllers/versions_controller_spec.rb
  17. +6 −4 spec/factories.rb
  18. +0 −110 spec/fixtures/events.yml
  19. +0 −25 spec/fixtures/sources.yml
  20. +0 −77 spec/fixtures/venues.yml
  21. +88 −0 spec/helpers/events_helper_spec.rb
  22. +104 −68 spec/models/event_spec.rb
  23. +0 −2  spec/models/source_parser_hcal_spec.rb
  24. +0 −2  spec/models/source_parser_upcoming_spec.rb
  25. +30 −47 spec/models/venue_spec.rb
  26. +20 −6 spec/spec_helper.rb
  27. +2 −2 spec/spec_helper_extensions.rb
  28. +2 −2 themes/default/views/site/_description.html.erb
1  Gemfile
View
@@ -108,6 +108,7 @@ group :development, :test do
gem 'webrat', '0.7.3'
gem 'factory_girl_rails', '1.7.0' # 2.0 and above don't support Ruby 1.8.7 :(
gem 'spork', '~> 0.9.2'
+ gem 'database_cleaner', '~> 0.8.0'
# Do not install these interactive libraries onto the continuous integration server.
unless ENV['CI'] || ENV['TRAVIS']
2  app/controllers/versions_controller.rb
View
@@ -1,7 +1,7 @@
class VersionsController < ApplicationController
def edit
@version = Version.find(params[:id])
- @record = @version.next.try(:reify) || @version.item
+ @record = @version.next.try(:reify) || @version.item || @version.reify
singular = @record.class.name.singularize.underscore
plural = @record.class.name.pluralize.underscore
60 app/helpers/events_helper.rb
View
@@ -61,7 +61,7 @@ def events_sort_label(key)
end
end
- #---[ Google Calendar exporting ]-----------------------------------------
+ #---[ Google Calendar export ]--------------------------------------------
# Time format used for Google Calendar exports
GOOGLE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"
@@ -102,4 +102,62 @@ def google_event_export_link(event)
return result
end
+ #---[ Feed links ]------------------------------------------------------
+
+ # Returns a URL for an events feed.
+ #
+ # @param [Hash] filter Options for filtering. If values are defined, returns
+ # a link to all events. If a :query is defined, returns a link to search
+ # events' text by that query. If a :tag is defined, returns a link to search
+ # events with that tag.
+ # @param [Hash] common Options for the URL helper, such as :protocol, :format
+ # and such.
+ #
+ # @raise [ArgumentError] Raised if given invalid filter options.
+ #
+ # @return [String] URL
+ def _events_feed_linker(filter={}, common={})
+ # Delete blank filter options because this method is typically called with
+ # both a :tag and :query filter, but only one will actually be set.
+ filter.delete_if { |key, value| value.blank? }
+
+ if (unknown = filter.keys - [:query, :tag]).present?
+ raise ArgumentError, "Unknown option(s): #{unknown.inspect}"
+ end
+
+ return filter.present? ?
+ search_events_url(common.merge(filter)) :
+ events_url(common)
+ end
+
+ GOOGLE_EVENT_SUBSCRIBE_BASE = "http://www.google.com/calendar/render?cid="
+
+ # Returns a Google Calendar subscription URL.
+ #
+ # @see #_events_feed_linker for details on parameters and exceptions.
+ def google_events_subscription_link(filter={})
+ link = _events_feed_linker(filter, :format => "ics")
+ return "#{GOOGLE_EVENT_SUBSCRIBE_BASE}#{CGI::escape(link)}"
+ end
+
+ # Returns an iCalendar subscription URL.
+ #
+ # @see #_events_feed_linker for details on parameters and exceptions.
+ def icalendar_feed_link(filter={})
+ return _events_feed_linker(filter, :protocol => "webcal", :format => "ics")
+ end
+
+ # Returns an iCalendar export URL.
+ #
+ # @see #_events_feed_linker for details on parameters and exceptions.
+ def icalendar_export_link(filter={})
+ return _events_feed_linker(filter, :format => "ics")
+ end
+
+ # Returns an ATOM subscription URL.
+ #
+ # @see #_events_feed_linker for details on parameters and exceptions.
+ def atom_feed_link(filter={})
+ return _events_feed_linker(filter, :format => "atom")
+ end
end
43 app/models/event.rb
View
@@ -55,11 +55,13 @@ class Event < ActiveRecord::Base
include ValidatesBlacklistOnMixin
validates_blacklist_on :title, :description, :url
+ include UpdateUpdatedAtMixin
+
include VersionDiff
# Duplicates
include DuplicateChecking
- duplicate_checking_ignores_attributes :source_id, :version
+ duplicate_checking_ignores_attributes :source_id, :version, :venue_id
duplicate_squashing_ignores_associations :tags, :base_tags, :taggings
# Named scopes
@@ -126,33 +128,42 @@ def end_time_with_smarter_setter=(value)
alias_method_chain :end_time=, :smarter_setter
end
- # Set the time in Event +record+ instance for an +attribute+ (e.g.,
- # :start_time) to +value+ (e.g., a Time).
+ # Sets record's attribute to time value. If time is invalid, marks record as invalid.
+ #
+ # @param [ActiveRecord::Base] record The record to modify.
+ # @param [String, Symbol] attribute The attribute to set, e.g. :start_time.
+ # @param [Time] value The time.
+ #
+ # @return [Time]
def self.set_time_on(record, attribute, value)
- result = self.time_for(value)
- case result
- when Exception
+ begin
+ result = self.time_for(value)
+ rescue Exception => e
record.errors.add(attribute, "is invalid")
return record.send("#{attribute}_without_smarter_setter=", nil)
- else
- return record.send("#{attribute}_without_smarter_setter=", result)
end
+ return record.send("#{attribute}_without_smarter_setter=", result)
end
- # Return the time for the +value+, which could be a Time, Date, DateTime,
- # String, Array of Strings, etc.
+ # Returns time for the value, which can be a Time, Date, DateTime, String,
+ # Array of Strings, nil, etc.
+ #
+ # @param [nil, String, Date, DateTime, ActiveSupport::TimeWithZone, Time] value The time to parse.
+ #
+ # @return [Time]
+ #
+ # @raise TypeError Thrown if given an unknown type.
+ # @raise Exception Thrown if value can't be parsed.
def self.time_for(value)
value = value.join(' ') if value.kind_of?(Array)
case value
when NilClass
return nil
when String
- return nil if value.blank?
- begin
- return Time.parse(value)
- rescue Exception => e
- return e
- end
+ # This can throw an exception
+ return value.present? ?
+ Time.parse(value) :
+ nil
when Date, DateTime, ActiveSupport::TimeWithZone
return value.to_time
when Time
6 app/models/update_updated_at_mixin.rb
View
@@ -0,0 +1,6 @@
+# WTF: Rails 3 no longer updates the #updated_at on #save, only on create or through #update_attributes. That's really stupid.
+module UpdateUpdatedAtMixin
+ def self.included(base)
+ base.send:before_save, lambda { |record| record.updated_at = Time.zone.now }
+ end
+end
2  app/models/venue.rb
View
@@ -61,6 +61,8 @@ class Venue < ActiveRecord::Base
include ValidatesBlacklistOnMixin
validates_blacklist_on :title, :description, :address, :url, :street_address, :locality, :region, :postal_code, :country, :email, :telephone
+ include UpdateUpdatedAtMixin
+
# Duplicates
include DuplicateChecking
duplicate_checking_ignores_attributes :source_id, :version, :closed, :wifi, :access_notes
2  app/views/events/index.atom.builder
View
@@ -2,7 +2,7 @@ cache_if(@perform_caching, CacheObserver.daily_key_for("events_atom", request))
atom_feed("xmlns:georss".to_sym => "http://www.georss.org/georss") do |feed|
feed.title("#{SETTINGS.name}#{': ' + @page_title if @page_title}")
unless @events.size == 0
- feed.updated(@events.sort_by(&:updated_at).last.updated_at)
+ feed.updated(@events.present? ? @events.sort_by(&:updated_at).last.updated_at : Time.now)
for event in @events
feed.entry(event) do |entry|
9 app/views/events/index.html.erb
View
@@ -31,15 +31,14 @@
<h3>Subscribe to</h3>
<ul>
- <li><%= link_to "iCalendar feed", url_for(:action => "index", :protocol => "webcal", :only_path => false)+".ics" %></li>
- <li><%= link_to "Atom feed", events_url(:format => "atom") %></li>
- <li><%= link_to "Google Calendar", "http://www.google.com/calendar/render?cid="+events_url(:format => 'ics') %></li>
+ <li><%= link_to "iCalendar feed", icalendar_feed_link %></li>
+ <li><%= link_to "Atom feed", atom_feed_link %></li>
+ <li><%= link_to "Google Calendar", google_events_subscription_link %></li>
</ul>
<h3>Export to</h3>
<ul>
- <li><%= link_to "iCalendar file", events_url(:format => "ics") %></li>
- <li><%= link_to "Google Calendar", "http://www.google.com/calendar/render?cid="+events_url(:format => 'ics') %></li>
+ <li><%= link_to "iCalendar file", icalendar_export_link %></li>
</ul>
</div>
11 app/views/events/search.html.erb
View
@@ -1,7 +1,7 @@
<% tabindex_on '#search_field' %>
<% content_for :discovery_insert do %>
- <%= auto_discovery_link_tag(:atom, search_events_url(:query => @query, :format => 'atom'), :title => "Atom: Search Results for '#{@query}'" )%>
+ <%= auto_discovery_link_tag(:atom, atom_feed_link(:query => @query, :tag => @tag), :title => "Atom: Search Results for '#{@query}'" )%>
<% end -%>
<%= render :partial => 'search_section', :locals => {:description => "current", :events => @grouped_events[:current]} %>
<%= render :partial => 'search_section', :locals => {:description => "past", :events => @grouped_events[:past]} %>
@@ -9,14 +9,13 @@
<div id='list_filters' class='sidebar'>
<h3>Subscribe to</h3>
<ul>
- <li><%= link_to "iCalendar feed", url_for(:action => "search", :protocol => "webcal", :only_path => false, :query => @query, :format => "ics") %></li>
- <li><%= link_to "Atom feed", search_events_url(:query => @query, :format => "atom") %></li>
- <li><%= link_to "Google Calendar", "http://www.google.com/calendar/render?cid="+CGI::escape(search_events_url(:query => @query, :format => "ics")) %></li>
+ <li><%= link_to "iCalendar feed", icalendar_feed_link(:query => @query, :tag => @tag) %></li>
+ <li><%= link_to "Atom feed", atom_feed_link(:query => @query, :tag => @tag) %></li>
+ <li><%= link_to "Google Calendar", google_events_subscription_link(:query => @query, :tag => @tag) %></li>
</ul>
<h3>Export to</h3>
<ul>
- <li><%= link_to "iCalendar file", search_events_url(:query => @query, :format => "ics") %></li>
- <li><%= link_to "Google Calendar", "http://www.google.com/calendar/render?cid="+cgi_escape(search_events_url(:query => @query, :format => "ics")) %></li>
+ <li><%= link_to "iCalendar file", icalendar_export_link(:query => @query, :tag => @tag) %></li>
</ul>
</div>
5 config/database~mysql.sample.yml
View
@@ -1,5 +1,4 @@
-<% puts "WARNING: MySQL support is incomplete at this time, use at your own risk!" unless $dyW; $dyW = true %>
-# MySQL. Versions 4.1 and 5.0 are recommended.
+# MySQL. Versions 5.x are recommended.
#
# Install the MySQL driver:
# gem install mysql
@@ -17,7 +16,7 @@ common: &common
adapter: mysql2
encoding: utf8
socket: <%= `mysql_config --socket`.chomp %>
- username: <%= ENV['DBUSER'] || ENV['USER'] %>
+ username: <%= ENV['DBUSER_MYSQL'] || ENV['DBUSER'] || ENV['USER'] %>
password:
development:
2  config/database~postgresql.sample.yml
View
@@ -8,7 +8,7 @@
common: &common
adapter: postgresql
encoding: unicode
- username: <%= ENV['DBUSER'] || ENV['USER'] %>
+ username: <%= ENV['DBUSER_POSTGRESQL'] || ENV['DBUSER'] || ENV['USER'] %>
password:
# Connect on a TCP socket. Omitted by default since the client uses a
15 db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb
View
@@ -0,0 +1,15 @@
+class SpecifyVenuesLatitudeAndLongitudePrecision < ActiveRecord::Migration
+ FIELDS = %w[latitude longitude].map(&:to_sym)
+
+ def up
+ for field in FIELDS
+ change_column :venues, field, :decimal, :precision => 7, :scale => 4
+ end
+ end
+
+ def down
+ for field in FIELDS
+ change_column :venues, field, :decimal
+ end
+ end
+end
55 lib/tasks/spec_db.rake
View
@@ -0,0 +1,55 @@
+namespace :spec do
+ namespace :db do
+ databases = [:postgresql, :mysql, :sqlite3]
+
+ desc "Run specs against all databases"
+ task :all do
+ failed = false
+
+ databases.each do |database|
+ failed = true unless test_against database
+ end
+
+ if failed
+ fail "## Error: At least one of the specs above failed"
+ end
+ end
+
+ databases.each do |database|
+ desc "Run specs against '#{database}' database"
+ task database do
+ return test_against database
+ end
+ end
+
+ def test_against(kind)
+ sample = Rails.root + "config/database~#{kind}.sample.yml"
+ custom = Rails.root + "config/database~custom.yml"
+ backup = Rails.root + "config/database~custom.yml.backup"
+
+ if custom.exist?
+ mv custom, backup
+ end
+
+ cp sample, custom
+
+ result = nil
+
+ begin
+ Rails.root.chdir do
+ puts
+ puts "## Database: #{kind}"
+ result = system "bundle --quiet && rake db:create:all db:migrate db:test:prepare spec"
+ end
+ ensure
+ rm custom
+
+ if backup.exist?
+ mv backup, custom
+ end
+ end
+
+ return result
+ end
+ end
+end
1,188 spec/controllers/events_controller_spec.rb
View
@@ -1,708 +1,824 @@
require 'spec_helper'
-describe EventsController, "when displaying index" do
- render_views
- fixtures :all
-
- it "should produce HTML" do
- get :index, :format => "html"
+describe EventsController do
+ describe "#index" do
+ render_views
- response.should have_selector("table.event_table")
- end
+ describe "as HTML" do
+ it "should produce HTML" do
+ get :index, :format => "html"
- describe "in XML format" do
+ response.should have_selector("table.event_table")
+ end
+ end
- it "should produce XML" do
- post :index, :format => "xml"
+ describe "as XML" do
+ describe "without events" do
+ before do
+ get :index, :format => "xml"
- hash = Hash.from_xml(response.body)
- hash["events"].should be_a_kind_of(Array)
- end
+ @struct = Hash.from_xml(response.body)["events"]
+ end
- it "should include venue details" do
- post :index, :format => "xml"
+ it "should not have entries" do
+ @struct.should be_blank
+ end
+ end
- hash = Hash.from_xml(response.body)
+ describe "with events" do
+ before do
+ Factory(:event)
+ Factory(:event)
- event = hash["events"].first
- venue = event["venue"]
- venue_title = venue["title"] # Why XML? Why?
- venue_title.should be_a_kind_of(String)
- venue_title.length.should > 0
- end
+ get :index, :format => "xml"
- end
+ @struct = Hash.from_xml(response.body)["events"]
+ end
- describe "in JSON format" do
+ it "should return an array" do
+ @struct.should be_a_kind_of(Array)
+ end
- it "should produce JSON" do
- post :index, :format => "json"
+ it "should have entries" do
+ @struct.should be_present
+ end
- struct = ActiveSupport::JSON.decode(response.body)
- struct.should be_a_kind_of(Array)
+ it "should include venue details" do
+ event = @struct.first
+ venue = event["venue"]
+ venue_title = venue["title"] # Why XML? Why?
+ venue_title.should be_a_kind_of(String)
+ venue_title.length.should > 0
+ end
+ end
end
- it "should accept a JSONP callback" do
- post :index, :format => "json", :callback => "some_function"
+ describe "as JSON" do
+ it "should accept a JSONP callback" do
+ post :index, :format => "json", :callback => "some_function"
- response.body.split("\n").join.should match(/^\s*some_function\(.*\);?\s*$/)
- end
+ response.body.split("\n").join.should match(/^\s*some_function\(.*\);?\s*$/)
+ end
- it "should include venue details" do
- post :index, :format => "json"
+ describe "without events" do
+ before do
+ post :index, :format => "json"
- struct = ActiveSupport::JSON.decode(response.body)
- event = struct.first
- event["venue"]["title"].should be_a_kind_of(String)
- event["venue"]["title"].length.should > 0
- end
+ @struct = ActiveSupport::JSON.decode(response.body)
+ end
- end
+ it "should return an array" do
+ @struct.should be_a_kind_of(Array)
+ end
- it "should produce ATOM" do
- post :index, :format => "atom"
+ it "should not have entries" do
+ @struct.should be_empty
+ end
+ end
- hash = Hash.from_xml(response.body)
- entries = hash["feed"]["entry"]
+ describe "with events" do
+ before do
+ @event = Factory(:event)
+ @venue = @event.venue
- entries.should be_a_kind_of(Array)
- entries.should_not be_empty
- entry = entries.first
- record = Event.find(entry['id'][%r{(\d+)$}, 1])
+ post :index, :format => "json"
- Nokogiri.parse(entry['content']).search('.description p').inner_html.should == record.description
- entry['end_time'].should == record.end_time.xmlschema
- entry['start_time'].should == record.start_time.xmlschema
- entry['summary'].should be_present
- entry['title'].should == record.title
- entry['updated'].should == record.updated_at.xmlschema
- entry['url'].should == event_url(record)
- end
+ @struct = ActiveSupport::JSON.decode(response.body)
+ end
- describe "in ICS format" do
+ it "should return an array" do
+ @struct.should be_a_kind_of(Array)
+ end
- it "should produce ICS" do
- post :index, :format => "ics"
+ it "should return an event" do
+ event = @struct.first
+ event['id'].should == @event.id
+ event['title'].should == @event.title
+ end
- response.body.should =~ /BEGIN:VEVENT/
- end
+ it "should return an event's venue" do
+ event = @struct.first
+ venue = event['venue']
- it "should render all future events" do
- post :index, :format => "ics"
- response.body.should =~ /SUMMARY:#{events(:tomorrow).title}/
- response.body.should_not =~ /SUMMARY:#{events(:old_event).title}/
+ venue['id'].should == @venue.id
+ venue['title'].should == @venue.title
+ end
+ end
end
- end
-
- describe "and filtering by date range" do
- [:start, :end].each do |date_kind|
- describe "for #{date_kind} date" do
- before :each do
- @date_kind = date_kind
- @date_kind_other = \
- case date_kind
- when :start then :end
- when :end then :start
- else raise ArgumentError, "Unknown date_kind: #{date_kind}"
- end
+ describe "as ATOM" do
+ describe "without events" do
+ before do
+ post :index, :format => "atom"
+ @struct = Hash.from_xml(response.body)
end
- it "should use the default if not given the parameter" do
- get :index, :date => {}
- assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
- flash[:failure].should be_nil
+ it "should be a feed" do
+ @struct['feed']['xmlns'].should be_present
end
- it "should use the default if given a malformed parameter" do
- get :index, :date => "omgkittens"
- assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
- response.should have_selector(".flash_failure", :content => 'malformed')
+ it "should not have events" do
+ @struct['feed']['entry'].should be_blank
end
+ end
- it "should use the default if given a missing parameter" do
- get :index, :date => {:foo => "bar"}
- assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
- response.should have_selector(".flash_failure", :content => 'missing')
+ describe "with events" do
+ before do
+ Factory(:event)
+ Factory(:event)
+
+ post :index, :format => "atom"
+
+ @struct = Hash.from_xml(response.body)
end
- it "should use the default if given an empty parameter" do
- get :index, :date => {@date_kind => ""}
- assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
- response.should have_selector(".flash_failure", :content => 'empty')
+ let(:entries) { @struct["feed"]["entry"] }
+
+ it "should be a feed" do
+ @struct['feed']['xmlns'].should be_present
end
- it "should use the default if given an invalid parameter" do
- get :index, :date => {@date_kind => "omgkittens"}
- assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
- response.should have_selector(".flash_failure", :content => 'invalid')
+ it "should have entries" do
+ entries.should be_present
end
- it "should use the value if valid" do
- expected = Date.yesterday
- get :index, :date => {@date_kind => expected.to_s("%Y-%m-%d")}
- assigns["#{@date_kind}_date"].should == expected
+ it "should have an event" do
+ entry = entries.first
+ record = Event.find(entry['id'][%r{(\d+)$}, 1])
+
+ Nokogiri.parse(entry['content']).search('.description p').inner_html.should == record.description
+ entry['end_time'].should == record.end_time.xmlschema
+ entry['start_time'].should == record.start_time.xmlschema
+ entry['summary'].should be_present
+ entry['title'].should == record.title
+ entry['updated'].should == record.updated_at.xmlschema
+ entry['url'].should == event_url(record)
end
end
end
- it "should return matching events" do
- # Given
- matching = [
- Event.create!(
- :title => "matching1",
- :start_time => Time.zone.parse("2010-01-16 00:00"),
- :end_time => Time.zone.parse("2010-01-16 01:00")
- ),
- Event.create!(:title => "matching2",
- :start_time => Time.zone.parse("2010-01-16 23:00"),
- :end_time => Time.zone.parse("2010-01-17 00:00")
- ),
- ]
-
- non_matching = [
- Event.create!(
- :title => "nonmatchingbefore",
- :start_time => Time.zone.parse("2010-01-15 23:00"),
- :end_time => Time.zone.parse("2010-01-15 23:59")
- ),
- Event.create!(
- :title => "nonmatchingafter",
- :start_time => Time.zone.parse("2010-01-17 00:01"),
- :end_time => Time.zone.parse("2010-01-17 01:00")
- ),
- ]
-
- # When
- get :index, :date => {:start => "2010-01-16", :end => "2010-01-16"}
- results = assigns[:events]
-
- # Then
- results.size.should == 2
- results.should == matching
- end
- end
-end
+ describe "as iCalendar" do
+ describe "without events" do
+ before do
+ post :index, :format => "ics"
+ end
-describe EventsController, "when displaying events" do
- it "should show an event" do
- event = Event.new(:start_time => Time.now)
- Event.should_receive(:find).and_return(event)
+ it "should have a calendar" do
+ response.body.should =~ /BEGIN:VCALENDAR/
+ end
- get "show", :id => 1234
- response.should be_success
- end
+ it "should not have events" do
+ response.body.should_not =~ /BEGIN:VEVENT/
+ end
+ end
- it "should redirect from a duplicate event to its master" do
- master = mock_model(Event, :id => 4321)
- event = Event.new(:start_time => Time.now, :duplicate_of => master)
- Event.should_receive(:find).and_return(event)
+ describe "with events" do
+ before do
+ @current_event = Factory(:event, :start_time => Time.now + 1.day)
+ @past_event = Factory(:event, :start_time => Time.now - 1.day)
- get "show", :id => 1234
- response.should redirect_to(event_path(master))
- end
+ post :index, :format => "ics"
+ end
- it "should show an error when asked to display a non-existent event" do
- Event.should_receive(:find).and_raise(ActiveRecord::RecordNotFound)
+ it "should have a calendar" do
+ response.body.should =~ /BEGIN:VCALENDAR/
+ end
- get "show", :id => 1234
- response.should redirect_to(events_path)
- flash[:failure].should_not be_blank
- end
-end
+ it "should have events" do
+ response.body.should =~ /BEGIN:VEVENT/
+ end
+
+ it "should render all future events" do
+ response.body.should =~ /SUMMARY:#{@current_event.title}/
+ response.body.should_not =~ /SUMMARY:#{@past_event.title}/
+ end
+ end
+ end
+
+ describe "and filtering by date range" do
+ [:start, :end].each do |date_kind|
+ describe "for #{date_kind} date" do
+ before :each do
+ @date_kind = date_kind
+ @date_kind_other = \
+ case date_kind
+ when :start then :end
+ when :end then :start
+ else raise ArgumentError, "Unknown date_kind: #{date_kind}"
+ end
+ end
+
+ it "should use the default if not given the parameter" do
+ get :index, :date => {}
+ assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
+ flash[:failure].should be_nil
+ end
+
+ it "should use the default if given a malformed parameter" do
+ get :index, :date => "omgkittens"
+ assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
+ response.should have_selector(".flash_failure", :content => 'malformed')
+ end
+
+ it "should use the default if given a missing parameter" do
+ get :index, :date => {:foo => "bar"}
+ assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
+ response.should have_selector(".flash_failure", :content => 'missing')
+ end
+
+ it "should use the default if given an empty parameter" do
+ get :index, :date => {@date_kind => ""}
+ assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
+ response.should have_selector(".flash_failure", :content => 'empty')
+ end
+
+ it "should use the default if given an invalid parameter" do
+ get :index, :date => {@date_kind => "omgkittens"}
+ assigns["#{@date_kind}_date"].should == controller.send("default_#{@date_kind}_date")
+ response.should have_selector(".flash_failure", :content => 'invalid')
+ end
+
+ it "should use the value if valid" do
+ expected = Date.yesterday
+ get :index, :date => {@date_kind => expected.to_s("%Y-%m-%d")}
+ assigns["#{@date_kind}_date"].should == expected
+ end
+ end
+ end
-describe EventsController, "when creating or updating events" do
- fixtures :all
-
- before(:each) do
- # Fields marked with "###" may be filled in by examples to alter behavior
- @params = {
- :end_date => "2008-06-04",
- :start_date => "2008-06-03",
- :event => {
- "title" => "MyVenue",
- "url" => "http://my.venue",
- "description" => "Wheeeee"
- },
- :end_time => "",
- :start_time => ""
- }
- @venue = mock_model(Venue)
- @event = mock_model(Event, {
- :title => "MyEvent",
- :start_time= => true,
- :end_time= => true,
- })
+ it "should return matching events" do
+ # Given
+ matching = [
+ Event.create!(
+ :title => "matching1",
+ :start_time => Time.zone.parse("2010-01-16 00:00"),
+ :end_time => Time.zone.parse("2010-01-16 01:00")
+ ),
+ Event.create!(:title => "matching2",
+ :start_time => Time.zone.parse("2010-01-16 23:00"),
+ :end_time => Time.zone.parse("2010-01-17 00:00")
+ ),
+ ]
+
+ non_matching = [
+ Event.create!(
+ :title => "nonmatchingbefore",
+ :start_time => Time.zone.parse("2010-01-15 23:00"),
+ :end_time => Time.zone.parse("2010-01-15 23:59")
+ ),
+ Event.create!(
+ :title => "nonmatchingafter",
+ :start_time => Time.zone.parse("2010-01-17 00:01"),
+ :end_time => Time.zone.parse("2010-01-17 01:00")
+ ),
+ ]
+
+ # When
+ get :index, :date => {:start => "2010-01-16", :end => "2010-01-16"}
+ results = assigns[:events]
+
+ # Then
+ results.size.should == 2
+ results.should == matching
+ end
+ end
end
- describe "when creating events" do
- render_views
+ describe "#show" do
+ it "should show an event" do
+ event = Event.new(:start_time => Time.now)
+ Event.should_receive(:find).and_return(event)
- it "should display form for creating new event" do
- get "new"
+ get "show", :id => 1234
response.should be_success
- response.should render_template(:new)
end
- it "should create a new event without a venue" do
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(nil)
- @event.should_receive(:save).and_return(true)
+ it "should redirect from a duplicate event to its master" do
+ master = Factory.build(:event, :id => 4321)
+ event = Event.new(:start_time => Time.now, :duplicate_of => master)
+ Event.should_receive(:find).and_return(event)
- post "create", @params
- response.should redirect_to(event_path(@event))
+ get "show", :id => 1234
+ response.should redirect_to(event_path(master))
end
- it "should associate a venue based on a given venue id" do
- @params[:event]["venue_id"] = @venue.id.to_s
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.should_receive(:associate_with_venue).with(@venue.id)
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:save).and_return(true)
+ it "should show an error when asked to display a non-existent event" do
+ Event.should_receive(:find).and_raise(ActiveRecord::RecordNotFound)
- post "create", @params
+ get "show", :id => 1234
+ response.should redirect_to(events_path)
+ flash[:failure].should_not be_blank
end
+ end
- it "should associate a venue based on a given venue name" do
- @params[:venue_name] = "Some Event"
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.should_receive(:associate_with_venue).with("Some Event")
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:save).and_return(true)
+ describe "when creating and updating events" do
+ before(:each) do
+ # Fields marked with "###" may be filled in by examples to alter behavior
+ @params = {
+ :end_date => "2008-06-04",
+ :start_date => "2008-06-03",
+ :event => {
+ "title" => "MyVenue",
+ "url" => "http://my.venue",
+ "description" => "Wheeeee"
+ },
+ :end_time => "",
+ :start_time => ""
+ }
+ @venue = Factory.build(:venue, :id => 12)
+ @event = Factory.build(:event, :id => 34, :venue => @venue)
+ end
- post "create", @params
+ describe "#new" do
+ it "should display form for creating new event" do
+ get "new"
+ response.should be_success
+ response.should render_template(:new)
+ end
end
- it "should associate a venue by id when both an id and a name are provided" do
- @params[:event]["venue_id"] = @venue.id.to_s
- @params[:venue_name] = "Some Event"
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.should_receive(:associate_with_venue).with(@venue.id)
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:save).and_return(true)
+ describe "#create" do
+ render_views
- post "create", @params
- end
+ it "should create a new event without a venue" do
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(nil)
+ @event.should_receive(:save).and_return(true)
- it "should create a new event for an existing venue" do
- @params[:venue_name] = "Old Venue"
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:save).and_return(true)
- @venue.stub!(:new_record?).and_return(false)
+ post "create", @params
+ response.should redirect_to(event_path(@event))
+ end
- post "create", @params
- response.should redirect_to(event_path(@event))
- end
+ it "should associate a venue based on a given venue id" do
+ @params[:event]["venue_id"] = @venue.id.to_s
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.should_receive(:associate_with_venue).with(@venue.id)
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:save).and_return(true)
- it "should create a new event and new venue, and redirect to venue edit form" do
- @params[:venue_name] = "New Venue"
- Event.should_receive(:new).with(@params[:event]).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:save).and_return(true)
- @venue.stub!(:new_record?).and_return(true)
+ post "create", @params
+ end
- post "create", @params
- response.should redirect_to(edit_venue_url(@venue, :from_event => @event.id))
- end
+ it "should associate a venue based on a given venue name" do
+ @params[:venue_name] = "My Venue"
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.should_receive(:associate_with_venue).with("My Venue")
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:save).and_return(true)
- it "should catch errors and redisplay the new event form" do
- post "create"
- response.should render_template(:new)
- end
+ post "create", @params
+ end
- it "should stop evil robots" do
- post "create", :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
- response.should render_template(:new)
- flash[:failure].should match(/evil robot/i)
- end
-
- it "should allow the user to preview the event" do
- event = Event.new(:title => "Awesomeness")
- Event.should_receive(:new).and_return(event)
-
- event.should_not_receive(:save)
-
- post "create", :event => { :title => "Awesomeness" },
- :start_time => Time.now, :start_date => Date.today,
- :end_time => Time.now, :end_date => Date.today,
- :preview => "Preview",
- :venue_name => "This venue had better not exist"
- response.should render_template(:new)
- response.should have_selector('#event_preview')
- event.should be_valid
- end
- end
+ it "should associate a venue by id when both an id and a name are provided" do
+ @params[:event]["venue_id"] = @venue.id.to_s
+ @params[:venue_name] = "Some Event"
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.should_receive(:associate_with_venue).with(@venue.id)
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:save).and_return(true)
- describe "when updating events" do
- before(:each) do
- @event = mock_model(Event, {
- :title => "MyEvent",
- :start_time= => true,
- :end_time= => true,
- :associate_with_venue => true,
- :venue => @venue
- })
- Event.stub!(:find).and_return(@event)
- end
+ post "create", @params
+ end
- it "should display form for editing event" do
- Event.should_receive(:find).and_return(@event)
+ it "should create a new event for an existing venue" do
+ @params[:venue_name] = "Old Venue"
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:save).and_return(true)
+ @venue.stub!(:new_record?).and_return(false)
- get "edit", :id => 1
- response.should be_success
- response.should render_template(:edit)
- end
+ post "create", @params
+ response.should redirect_to(event_path(@event))
+ end
- it "should update an event without a venue" do
- Event.should_receive(:find).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(nil)
- @event.should_receive(:update_attributes).and_return(true)
+ it "should create a new event and new venue, and redirect to venue edit form" do
+ @params[:venue_name] = "New Venue"
+ Event.should_receive(:new).with(@params[:event]).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:save).and_return(true)
+ @venue.stub!(:new_record?).and_return(true)
- put "update", @params
- response.should redirect_to(event_path(@event))
- end
+ post "create", @params
+ response.should redirect_to(edit_venue_url(@venue, :from_event => @event.id))
+ end
- it "should associate a venue based on a given venue id" do
- @params[:event]["venue_id"] = @venue.id.to_s
- Event.should_receive(:find).and_return(@event)
- @event.should_receive(:associate_with_venue).with(@venue.id)
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:update_attributes).and_return(true)
+ it "should catch errors and redisplay the new event form" do
+ post "create"
+ response.should render_template(:new)
+ end
- post "update", @params
- end
+ it "should stop evil robots" do
+ post "create", :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
+ response.should render_template(:new)
+ flash[:failure].should match(/evil robot/i)
+ end
- it "should associate a venue based on a given venue name" do
- @params[:venue_name] = "Some Event"
- Event.should_receive(:find).and_return(@event)
- @event.should_receive(:associate_with_venue).with("Some Event")
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:update_attributes).and_return(true)
+ it "should allow the user to preview the event" do
+ event = Event.new(:title => "Awesomeness")
+ Event.should_receive(:new).and_return(event)
- post "update", @params
- end
+ event.should_not_receive(:save)
- it "should associate a venue by id when both an id and a name are provided" do
- @params[:event]["venue_id"] = @venue.id.to_s
- @params[:venue_name] = "Some Event"
- Event.should_receive(:find).and_return(@event)
- @event.should_receive(:associate_with_venue).with(@venue.id)
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:update_attributes).and_return(true)
+ post "create", :event => { :title => "Awesomeness" },
+ :start_time => Time.now, :start_date => Date.today,
+ :end_time => Time.now, :end_date => Date.today,
+ :preview => "Preview",
+ :venue_name => "This venue had better not exist"
+ response.should render_template(:new)
+ response.should have_selector('#event_preview')
+ event.should be_valid
+ end
- post "update", @params
- end
+ it "should create an event for an existing venue" do
+ venue = Factory(:venue)
- it "should update an event and associate it with an existing venue" do
- @params[:venue_name] = "Old Venue"
- Event.should_receive(:find).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:update_attributes).and_return(true)
- @venue.stub!(:new_record?).and_return(false)
+ post "create",
+ :start_time => Time.now.strftime("%Y-%m-%d"),
+ :end_time => (Time.now + 1.hour).strftime("%Y-%m-%d"),
+ :event => {
+ :title => "My Event",
+ :tag_list => ",,foo,bar, baz,",
+ },
+ :venue_name => venue.title
- put "update", @params
- response.should redirect_to(event_path(@event))
- end
+ response.should be_redirect
- it "should update an event and create a new venue, and redirect to the venue edit form" do
- @params[:venue_name] = "New Venue"
- Event.should_receive(:find).and_return(@event)
- @event.stub!(:associate_with_venue).with(@params[:venue_name])
- @event.stub!(:venue).and_return(@venue)
- @event.should_receive(:update_attributes).and_return(true)
- @venue.stub!(:new_record?).and_return(true)
+ flash[:success].should be_present
- put "update", @params
- response.should redirect_to(edit_venue_url(@venue, :from_event => @event.id))
+ event = assigns[:event]
+ event.title.should == "My Event"
+ event.venue.title.should == venue.title
+ event.venue.id.should == venue.id
+ event.tag_list.to_a.sort.should == %w(bar baz foo)
+ end
end
- it "should catch errors and redisplay the new event form" do
- Event.should_receive(:find).and_return(@event)
- @event.stub!(:associate_with_venue)
- @event.stub!(:venue).and_return(nil)
- @event.should_receive(:update_attributes).and_return(false)
+ describe "#update" do
+ before(:each) do
+ @event = Factory.build(:event, :id => 42)
+ @venue = @event.venue
+ Event.stub!(:find).and_return(@event)
+ end
- post "update", :id => 1234
- response.should render_template(:edit)
- end
+ it "should display form for editing event" do
+ Event.should_receive(:find).and_return(@event)
- it "should stop evil robots" do
- put "update", :id => 1234, :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
- response.should render_template(:edit)
- flash[:failure].should match(/evil robot/i)
- end
+ get "edit", :id => 1
+ response.should be_success
+ response.should render_template(:edit)
+ end
- it "should allow the user to preview the event" do
- tags = []
- tags.should_receive(:reload)
+ it "should update an event without a venue" do
+ Event.should_receive(:find).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(nil)
+ @event.should_receive(:update_attributes).and_return(true)
- Event.should_receive(:find).and_return(@event)
- @event.should_not_receive(:update_attributes)
- @event.should_receive(:attributes=)
- @event.should_receive(:valid?).and_return(true)
- @event.should_receive(:tags).and_return(tags)
+ put "update", @params
+ response.should redirect_to(event_path(@event))
+ end
- put "update", @params.merge(:preview => "Preview")
- response.should render_template(:edit)
- end
+ it "should associate a venue based on a given venue id" do
+ @params[:event]["venue_id"] = @venue.id.to_s
+ Event.should_receive(:find).and_return(@event)
+ @event.should_receive(:associate_with_venue).with(@venue.id)
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:update_attributes).and_return(true)
- end
+ post "update", @params
+ end
- describe "when cloning event" do
- fixtures :all
- before(:each) do
- @event = events(:calagator_codesprint)
- Event.stub!(:find).and_return(@event)
- get "clone", :id => 1
- end
+ it "should associate a venue based on a given venue name" do
+ @params[:venue_name] = "Some Event"
+ Event.should_receive(:find).and_return(@event)
+ @event.should_receive(:associate_with_venue).with("Some Event")
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:update_attributes).and_return(true)
- it "should use the cloned object" do
- record = assigns[:event]
- record.should be_a_new_record
- record.id.should be_nil
- end
+ post "update", @params
+ end
- it "should display a new event form" do
- response.should be_success
- response.should render_template(:new)
- end
+ it "should associate a venue by id when both an id and a name are provided" do
+ @params[:event]["venue_id"] = @venue.id.to_s
+ @params[:venue_name] = "Some Event"
+ Event.should_receive(:find).and_return(@event)
+ @event.should_receive(:associate_with_venue).with(@venue.id)
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:update_attributes).and_return(true)
- it "should have notice with cloning instructions" do
- flash[:success].should =~ /clone/i
- end
- end
-end
+ post "update", @params
+ end
-describe EventsController, "managing duplicates" do
- render_views
- fixtures :all
+ it "should update an event and associate it with an existing venue" do
+ @params[:venue_name] = "Old Venue"
+ Event.should_receive(:find).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:update_attributes).and_return(true)
+ @venue.stub!(:new_record?).and_return(false)
- it "should find new duplicates and not old duplicates" do
- get 'duplicates', :type => 'title'
+ put "update", @params
+ response.should redirect_to(event_path(@event))
+ end
- # New duplicates
- web3con = assigns[:grouped_events].select{|keys,values| keys.include?("Web 3.0 Conference")}
- web3con.should_not be_blank
- web3con.first.last.size.should == 2
+ it "should update an event and create a new venue, and redirect to the venue edit form" do
+ @params[:venue_name] = "New Venue"
+ Event.should_receive(:find).and_return(@event)
+ @event.stub!(:associate_with_venue).with(@params[:venue_name])
+ @event.stub!(:venue).and_return(@venue)
+ @event.should_receive(:update_attributes).and_return(true)
+ @venue.stub!(:new_record?).and_return(true)
- # Old duplicates
- web1con = assigns[:grouped_events].select{|keys,values| keys.include?("Web 1.0 Conference")}
- web1con.should be_blank
- end
+ put "update", @params
+ response.should redirect_to(edit_venue_url(@venue, :from_event => @event.id))
+ end
- it "should redirect duplicate events to their master" do
- event_master = events(:calagator_codesprint)
- event_duplicate = events(:tomorrow)
+ it "should catch errors and redisplay the new event form" do
+ Event.should_receive(:find).and_return(@event)
+ @event.stub!(:associate_with_venue)
+ @event.stub!(:venue).and_return(nil)
+ @event.should_receive(:update_attributes).and_return(false)
- get 'show', :id => event_duplicate.id
- response.should_not be_redirect
- assigns(:event).id.should == event_duplicate.id
+ post "update", :id => 1234
+ response.should render_template(:edit)
+ end
- event_duplicate.duplicate_of = event_master
- event_duplicate.save!
+ it "should stop evil robots" do
+ put "update", :id => 1234, :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
+ response.should render_template(:edit)
+ flash[:failure].should match(/evil robot/i)
+ end
- get 'show', :id => event_duplicate.id
- response.should be_redirect
- response.should redirect_to(event_url(event_master.id))
- end
+ it "should allow the user to preview the event" do
+ tags = []
+ tags.should_receive(:reload)
- it "should display an error message if given invalid arguments" do
- get 'duplicates', :type => 'omgwtfbbq'
+ Event.should_receive(:find).and_return(@event)
+ @event.should_not_receive(:update_attributes)
+ @event.should_receive(:attributes=)
+ @event.should_receive(:valid?).and_return(true)
+ @event.should_receive(:tags).and_return(tags)
- response.should be_success
- response.should have_selector('.failure', :content => 'omgwtfbbq')
- end
+ put "update", @params.merge(:preview => "Preview")
+ response.should render_template(:edit)
+ end
-end
+ end
-describe EventsController, "when searching" do
+ describe "#clone" do
+ before do
+ @event = Factory(:event)
- it "should search" do
- Event.should_receive(:search_keywords_grouped_by_currentness).and_return({:current => [], :past => []})
+ Event.stub!(:find).and_return(@event)
- post :search, :query => "myquery"
- end
+ get "clone", :id => 1
+ end
- it "should fail if given no search query" do
- post :search
+ it "should build an unsaved record" do
+ record = assigns[:event]
+ record.should be_a_new_record
+ record.id.should be_nil
+ end
- flash[:failure].should_not be_blank
- response.should redirect_to(root_path)
- end
+ it "should build a cloned record similar to the existing record" do
+ record = assigns[:event]
+ %w[title description venue_id venue_details].each do |field|
+ record.attributes[field].should == @event.attributes[field]
+ end
+ end
- it "should be able to only return current events" do
- Event.should_receive(:search).with("myquery", :order => nil, :skip_old => true).and_return([])
+ it "should display a new event form" do
+ response.should be_success
+ response.should render_template(:new)
+ end
- post :search, :query => "myquery", :current => "1"
+ it "should have notice with cloning instructions" do
+ flash[:success].should =~ /clone/i
+ end
+ end
end
- it "should be able to only return events matching specific tag" do
- Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
+ describe "#duplicates" do
+ render_views
+
+ it "should find current duplicates and not past duplicates" do
+ current_master = Factory(:event, :title => "Current")
+ current_duplicate = Factory(:event, :title => current_master.title)
- post :search, :tag => "foo"
- end
+ past_master = Factory(:event, :title => "Past", :start_time => Time.now - 2.days)
+ past_duplicate = Factory(:event, :title => past_master.title, :start_time => Time.now - 1.day)
- it "should warn if user tries ordering tags by score" do
- Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
+ get 'duplicates', :type => 'title'
- post :search, :tag => "foo", :order => "score"
- flash[:failure].should_not be_blank
- end
+ # Current duplicates
+ assigns[:grouped_events].select{|keys,values| keys.include?(current_master.title)}.tap do |events|
+ events.should_not be_empty
+ events.first.last.size.should == 2
+ end
+
+ # Past duplicates
+ assigns[:grouped_events].select{|keys,values| keys.include?(past_master.title)}.should be_empty
+ end
+
+ it "should redirect duplicate events to their master" do
+ event_master = Factory(:event)
+ event_duplicate = Factory(:event)
+
+ get 'show', :id => event_duplicate.id
+ response.should_not be_redirect
+ assigns(:event).id.should == event_duplicate.id
+
+ event_duplicate.duplicate_of = event_master
+ event_duplicate.save!
+
+ get 'show', :id => event_duplicate.id
+ response.should be_redirect
+ response.should redirect_to(event_url(event_master.id))
+ end
+
+ it "should display an error message if given invalid arguments" do
+ get 'duplicates', :type => 'omgwtfbbq'
- it "should warn if user tries ordering tags by invalid order" do
- Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
+ response.should be_success
+ response.should have_selector('.failure', :content => 'omgwtfbbq')
+ end
- post :search, :tag => "foo", :order => "kittens"
- flash[:failure].should_not be_blank
end
- describe "when returning results" do
- render_views
- fixtures :all
+ describe "#search" do
+ it "should search" do
+ Event.should_receive(:search_keywords_grouped_by_currentness).and_return({:current => [], :past => []})
- before do
- @results = {
- :current => [events(:calagator_codesprint), events(:tomorrow)],
- :past => [events(:old_event)],
- }
- Event.should_receive(:search_keywords_grouped_by_currentness).and_return(@results)
+ post :search, :query => "myquery"
end
- it "should produce HTML" do
- post :search, :query => "myquery", :format => "html"
+ it "should fail if given no search query" do
+ post :search
- response.should have_selector("table.event_table")
- assigns[:events].should == @results[:past] + @results[:current]
+ flash[:failure].should_not be_blank
+ response.should redirect_to(root_path)
end
- describe "in XML format" do
+ it "should be able to only return current events" do
+ Event.should_receive(:search).with("myquery", :order => nil, :skip_old => true).and_return([])
- it "should produce XML" do
- post :search, :query => "myquery", :format => "xml"
+ post :search, :query => "myquery", :current => "1"
+ end
- hash = Hash.from_xml(response.body)
- hash["events"].should be_a_kind_of(Array)
+ describe "by tag" do
+ it "should be able to only return events matching specific tag" do
+ Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
+
+ post :search, :tag => "foo"
end
- it "should include venue details" do
- post :search, :query => "myquery", :format => "xml"
+ it "should warn if user tries ordering tags by score" do
+ Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
- hash = Hash.from_xml(response.body)
- event = hash["events"].first
- venue = event["venue"]
- venue_title = venue["title"]
- venue_title.should be_a_kind_of(String)
- venue_title.length.should > 0
+ post :search, :tag => "foo", :order => "score"
+ flash[:failure].should_not be_blank
+ end
+
+ it "should warn if user tries ordering tags by invalid order" do
+ Event.should_receive(:tagged_with).with("foo", :order => "events.start_time").and_return([])
+
+ post :search, :tag => "foo", :order => "kittens"
+ flash[:failure].should_not be_blank
end
+ # TODO Add subscribe and other links
end
- describe "in JSON format" do
+ describe "when returning results" do
+ render_views
- it "should produce JSON" do
- post :search, :query => "myquery", :format => "json"
+ let(:current_event) { Factory(:event) }
+ let(:current_event_2) { Factory(:event) }
+ let(:past_event) { Factory(:event) }
+ let(:results) do
+ {
+ :current => [current_event, current_event_2],
+ :past => [past_event],
+ }
+ end
- struct = ActiveSupport::JSON.decode(response.body)
- struct.should be_a_kind_of(Array)
+ before do
+ Event.should_receive(:search_keywords_grouped_by_currentness).and_return(results)
end
- it "should accept a JSONP callback" do
- post :search, :query => "myquery", :format => "json", :callback => "some_function"
+ describe "in HTML format" do
+ before do
+ post :search, :query => "myquery", :format => "html"
+ end
- response.body.split("\n").join.should match(/^\s*some_function\(.*\);?\s*$/)
- end
+ it "should assign matching events" do
+ assigns[:events].should == results[:past] + results[:current]
+ end
+
+ it "should render matching events" do
+ have_selector "table.event_table" do
+ have_selector ".vevent a.summary", :href => event_url(results[:past])
+ have_selector ".vevent a.summary", :href => event_url(results[:current])
+ end
+ end
+
+ describe "sidebar" do
+ it "should have iCalendar feed" do
+ have_selector ".sidebar a", :href => search_events_url(:query => @query, :format => "ics", :protocol => "webcal")
+ end
- it "should include venue details" do
- post :search, :query => "myquery", :format => "json"
+ it "should have Atom feed" do
+ have_selector ".sidebar a", :href => search_events_url(:query => @query, :format => "atom")
+ end
- struct = ActiveSupport::JSON.decode(response.body)
- event = struct.first
- event["venue"]["title"].should be_a_kind_of(String)
- event["venue"]["title"].length.should > 0
+ it "should have Google subscription" # TODO
+ end
end
- end
+ describe "in XML format" do
- it "should produce ATOM" do
- post :search, :query => "myquery", :format => "atom"
+ it "should produce XML" do
+ post :search, :query => "myquery", :format => "xml"
- hash = Hash.from_xml(response.body)
- hash["feed"]["entry"].should be_a_kind_of(Array)
- end
+ hash = Hash.from_xml(response.body)
+ hash["events"].should be_a_kind_of(Array)
+ end
- describe "in ICS format" do
+ it "should include venue details" do
+ post :search, :query => "myquery", :format => "xml"
- it "should produce ICS" do
- post :search, :query => "myquery", :format => "ics"
+ hash = Hash.from_xml(response.body)
+ event = hash["events"].first
+ venue = event["venue"]
+ venue_title = venue["title"]
+ venue_title.should be_a_kind_of(String)
+ venue_title.length.should > 0
+ end
- response.body.should =~ /BEGIN:VEVENT/
end
- it "should produce events matching the query" do
- post :search, :query => "myquery", :format => "ics"
- response.body.should =~ /SUMMARY:#{events(:tomorrow).title}/
- response.body.should =~ /SUMMARY:#{events(:old_event).title}/
+ describe "in JSON format" do
+
+ it "should produce JSON" do
+ post :search, :query => "myquery", :format => "json"
+
+ struct = ActiveSupport::JSON.decode(response.body)
+ struct.should be_a_kind_of(Array)
+ end
+
+ it "should accept a JSONP callback" do
+ post :search, :query => "myquery", :format => "json", :callback => "some_function"
+
+ response.body.split("\n").join.should match(/^\s*some_function\(.*\);?\s*$/)
+ end
+
+ it "should include venue details" do
+ post :search, :query => "myquery", :format => "json"
+
+ struct = ActiveSupport::JSON.decode(response.body)
+ event = struct.first
+ event["venue"]["title"].should be_a_kind_of(String)
+ event["venue"]["title"].length.should > 0
+ end
+
end
- end
- end
-end
+ it "should produce ATOM" do
+ post :search, :query => "myquery", :format => "atom"
-describe EventsController, "when deleting" do
+ hash = Hash.from_xml(response.body)
+ hash["feed"]["entry"].should be_a_kind_of(Array)
+ end
- it "should destroy events" do
- event = mock_model(Event, :title => "Soon to be gone")
- event.should_receive(:destroy)
- Event.should_receive(:find).and_return(event)
+ describe "in ICS format" do
- delete 'destroy', :id => 1234
- response.should redirect_to(events_url)
- end
+ it "should produce ICS" do
+ post :search, :query => "myquery", :format => "ics"
-end
+ response.body.should =~ /BEGIN:VEVENT/
+ end
-describe EventsController, "when running integration test" do
- render_views
- fixtures :all
-
- before(:each) do
- @venue = venues(:cubespace)
- @event_params = {
- :title => "MyEvent#{$$}",
- :description => "Description",
- :start_time => Time.today.strftime("%Y-%m-%d"),
- :end_time => Time.today.strftime("%Y-%m-%d")
- }
- end
+ it "should produce events matching the query" do
+ post :search, :query => "myquery", :format => "ics"
+ response.body.should =~ /SUMMARY:#{current_event_2.title}/
+ response.body.should =~ /SUMMARY:#{past_event.title}/
+ end
- it "should create event for existing venue" do
- post "create",
- :start_time => @event_params[:start_time],
- :end_time => @event_params[:end_time],
- :event => @event_params,
- :venue_name => @venue.title
-
- flash[:success].should_not be_blank
- event = assigns[:event]
- event.title.should == @event_params[:title]
- event.venue.title.should == @venue.title
+ end
+ end
end
- it "should create event for exsiting venue and add tags" do
- post "create",
- :start_time => @event_params[:start_time],
- :end_time => @event_params[:end_time],
- :event => @event_params.merge(:tag_list => ",,foo,bar, baz,"),
- :venue_name => @venue.title
+ describe "#destroy" do
+ it "should destroy events" do
+ event = Factory.build(:event)
+ event.should_receive(:destroy)
+ Event.should_receive(:find).and_return(event)
- flash[:success].should_not be_blank
- event = assigns[:event]
- event.tag_list.to_a.sort.should == %w(bar baz foo)
+ delete 'destroy', :id => 1234
+ response.should redirect_to(events_url)
+ end
end
end
84 spec/controllers/venues_controller_spec.rb
View
@@ -2,7 +2,6 @@
describe VenuesController do
render_views
- fixtures :all
#Delete this example and add some real ones
it "should use VenuesController" do
@@ -10,16 +9,19 @@
end
it "should redirect duplicate venues to their master" do
- venue_master = venues(:cubespace)
- venue_duplicate = venues(:duplicate_venue)
+ venue_master = Factory(:venue)
+ venue_duplicate = Factory(:venue)
+ # No redirect when they're unique
get 'show', :id => venue_duplicate.id
response.should_not be_redirect
assigns(:venue).id.should == venue_duplicate.id
+ # Mark as duplicate
venue_duplicate.duplicate_of = venue_master
venue_duplicate.save!
+ # Now check that redirection happens
get 'show', :id => venue_duplicate.id
response.should be_redirect
response.should redirect_to(venue_url(venue_master.id))
@@ -31,20 +33,20 @@
response.should be_success
response.should have_selector('.failure', :content => 'omgwtfbbq')
end
-
+
describe "when creating venues" do
it "should stop evil robots" do
post :create, :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
response.should render_template(:new)
end
end
-
- describe "when updating venues" do
- before(:each) do
- @venue = stub_model(Venue, :versions => [])
+
+ describe "when updating venues" do
+ before do
+ @venue = Factory.build(:venue, :versions => [])
Venue.stub!(:find).and_return(@venue)
end
-
+
it "should stop evil robots" do
put :update,:id => '1', :trap_field => "I AM AN EVIL ROBOT, I EAT OLD PEOPLE'S MEDICINE FOR FOOD!"
response.should render_template(:edit)
@@ -52,16 +54,10 @@
end
describe "when rendering the venues index" do
- before :each do
- @open_venue = Venue.create!(:title => 'Open Town', :description => 'baz')
- @closed_venue = Venue.create!(:title => 'Closed Down', :closed => true)
- @wifi_venue = Venue.create!(:title => "Internetful", :wifi => true)
- end
-
- after :each do
- @open_venue.destroy
- @closed_venue.destroy
- @wifi_venue.destroy
+ before do
+ @open_venue = Factory(:venue, :title => 'Open Town', :description => 'baz', :wifi => false)
+ @closed_venue = Factory(:venue, :title => 'Closed Down', :closed => true, :wifi => false)
+ @wifi_venue = Factory(:venue, :title => "Internetful", :wifi => true)
end
describe "with no parameters" do
@@ -71,8 +67,8 @@
it "should assign @most_active_venues and @newest_venues by default" do
get :index
- assigns[:most_active_venues].should_not be_nil
- assigns[:newest_venues].should_not be_nil
+ assigns[:most_active_venues].should be_present
+ assigns[:newest_venues].should be_present
end
it "should not included closed venues" do
@@ -166,34 +162,37 @@
end
describe "when showing venues" do
-
- before(:each) do
- @venue = Venue.find(:first)
- end
-
describe "in JSON format" do
+ describe "with events" do
+ before do
+ @venue = Factory.build(:venue, :id => 123)
+ Venue.stub!(:find).and_return(@venue)
+ end
- it "should produce JSON" do
- get :show, :id => @venue.to_param, :format => "json"
+ it "should produce JSON" do
+ get :show, :id => @venue.to_param, :format => "json"