diff --git a/.gitignore b/.gitignore
index 4f6565a58..66ee0c067 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@
# vagrant instance folder
.vagrant
+
+Procfile
diff --git a/.rubocop.yml b/.rubocop.yml
index 177cf1034..9d5ee2345 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -28,10 +28,10 @@ Style/DotPosition:
EnforcedStyle: trailing
Style/EmptyLinesAroundClassBody:
- EnforcedStyle: empty_lines
+ Enabled: false
Style/EmptyLinesAroundModuleBody:
- EnforcedStyle: empty_lines
+ Enabled: false
Style/IndentationConsistency:
EnforcedStyle: rails
@@ -67,6 +67,12 @@ Style/BlockDelimiters:
Style/ClassAndModuleChildren:
Enabled: false
+Style/ExtraSpacing:
+ Enabled: false
+
+Style/SpaceAroundOperators:
+ Enabled: false
+
Metrics/AbcSize:
Max: 25
diff --git a/Gemfile b/Gemfile
index 2251dea34..bbdaf0710 100644
--- a/Gemfile
+++ b/Gemfile
@@ -132,7 +132,9 @@ group :development, :test do
gem 'guard-rubocop'
gem 'guard-bundler', require: false
gem 'capybara'
+ gem 'capybara-email'
gem 'pry'
gem 'factory_girl_rails'
- gem 'shoulda-matchers', require: false
+ gem 'shoulda-matchers', '~> 3.1.1', require: false
+ gem 'database_cleaner'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index a5cc9bca7..fe6008c85 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -68,6 +68,9 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
+ capybara-email (2.4.0)
+ capybara (~> 2.4)
+ mail
coderay (1.1.0)
coffee-rails (4.1.1)
coffee-script (>= 2.2.0)
@@ -78,6 +81,7 @@ GEM
coffee-script-source (1.10.0)
concurrent-ruby (1.0.0)
daemons (1.2.3)
+ database_cleaner (1.5.1)
debug_inspector (0.0.2)
delayed_job (4.1.1)
activesupport (>= 3.0, < 5.0)
@@ -316,7 +320,7 @@ GEM
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
shellany (0.0.1)
- shoulda-matchers (3.1.0)
+ shoulda-matchers (3.1.1)
activesupport (>= 4.0.0)
slop (3.6.0)
sprockets (3.5.2)
@@ -399,8 +403,10 @@ DEPENDENCIES
bullet
capistrano!
capybara
+ capybara-email
coffee-rails
daemons
+ database_cleaner
delayed_job_active_record
devise
factory_girl_rails
@@ -434,7 +440,7 @@ DEPENDENCIES
ruby-prof
rvm-capistrano
sass-rails
- shoulda-matchers
+ shoulda-matchers (~> 3.1.1)
sqlite3
stackprof
tire
diff --git a/Guardfile b/Guardfile
index 3f0ed6335..d31b4fd4b 100644
--- a/Guardfile
+++ b/Guardfile
@@ -1,6 +1,6 @@
# This group allows to skip running RuboCop when RSpec failed.
group :red_green_refactor, halt_on_fail: true do
- guard :rspec, cmd: "bundle exec rspec" do
+ guard :rspec, cmd: "bundle exec rspec --fail-fast" do
require "guard/rspec/dsl"
dsl = Guard::RSpec::Dsl.new(self)
@@ -41,9 +41,31 @@ group :red_green_refactor, halt_on_fail: true do
watch('spec/features/people_registration_spec.rb')
watch('app/controllers/public/people_controller.rb') { 'spec/features/people_registration_spec.rb' }
watch('app/views/public/people/new.html.erb') { 'spec/features/people_registration_spec.rb' }
+ watch('app/views/public/people/_form.html.erb') { 'spec/features/people_registration_spec.rb' }
+
+ watch('app/models/v2/event.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/models/v2/event_invitation.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/models/v2/time_window.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/models/v2/time_slot.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/mailers/event_invitation_mailer.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/controllers/v2/event_invitations_controller.rb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/views/v2/event_invitations/new.html.erb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/views/v2/event_invitations/_form.html.erb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/views/event_invitation_mailer/invite.html.erb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+ watch('app/views/event_invitation_mailer/invite.text.erb') { 'spec/features/invite_person_to_phone_call_spec.rb' }
+
+ watch('spec/features/person_responds_to_interview_invitation_spec.rb')
+ watch('app/views/event_invitation_mailer/invite.html.erb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+ watch('app/views/event_invitation_mailer/invite.text.erb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+
+ watch('app/mailers/event_invitation_mailer.rb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+ watch('app/models/v2/reservation.rb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+ watch('app/controllers/v2/reservations_controller.rb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+ watch('app/views/v2/reservations/new.html.erb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
+ watch('app/views/v2/reservations/_form.html.erb') { 'spec/features/person_responds_to_interview_invitation_spec.rb' }
end
- guard :minitest do
+ guard :minitest, test_folders: ['test'] do
watch(%r{^test/(.*)\/?test_(.*)\.rb$})
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
watch(%r{^test/test_helper\.rb$}) { 'test' }
diff --git a/app/controllers/v2/event_invitations_controller.rb b/app/controllers/v2/event_invitations_controller.rb
new file mode 100644
index 000000000..325040eab
--- /dev/null
+++ b/app/controllers/v2/event_invitations_controller.rb
@@ -0,0 +1,39 @@
+class V2::EventInvitationsController < ApplicationController
+ def new
+ @event_invitation = V2::EventInvitation.new
+ end
+
+ def create
+ @event_invitation = V2::EventInvitation.new(event_invitation_params)
+
+ if @event_invitation.save
+ send_notifications(@event_invitation)
+ flash[:notice] = 'Person was successfully invited.'
+ else
+ flash[:error] = 'There were problems with some of the fields.'
+ end
+
+ render :new
+ end
+
+ private
+
+ def send_notifications(event_invitation)
+ EventInvitationMailer.invite(
+ email_address: event_invitation.email_address,
+ event: event_invitation.event
+ ).deliver_later
+ end
+
+ def event_invitation_params
+ params.require(:v2_event_invitation).
+ permit(
+ :email_address,
+ :description,
+ :slot_length,
+ :date,
+ :start_time,
+ :end_time
+ )
+ end
+end
diff --git a/app/controllers/v2/reservations_controller.rb b/app/controllers/v2/reservations_controller.rb
new file mode 100644
index 000000000..24ab995ff
--- /dev/null
+++ b/app/controllers/v2/reservations_controller.rb
@@ -0,0 +1,48 @@
+class V2::ReservationsController < ApplicationController
+ skip_before_action :authenticate_user!
+
+ def new
+ event = V2::Event.find(event_params[:event_id])
+ @time_slots = event.time_slots
+ @person = Person.find_by(email_address: person_params[:email_address])
+ @reservation = V2::Reservation.new(time_slot: V2::TimeSlot.new)
+ end
+
+ def create
+ @reservation = V2::Reservation.new(reservation_params)
+
+ if @reservation.save
+ flash[:notice] = "An interview has been booked for #{@reservation.time_slot.to_weekday_and_time}"
+
+ send_notifications(@reservation)
+ else
+ flash[:error] = "No time slot was selected, couldn't create the reservation"
+ end
+
+ @time_slots = []
+ @person = @reservation.person
+
+ render :new
+ end
+
+ private
+
+ def send_notifications(reservation)
+ ReservationNotifier.notify(
+ email_address: reservation.person.email_address,
+ reservation: reservation
+ ).deliver_later
+ end
+
+ def event_params
+ params.permit(:event_id)
+ end
+
+ def reservation_params
+ params.require(:v2_reservation).permit(:person_id, :time_slot_id)
+ end
+
+ def person_params
+ params.permit(:email_address, :person_id)
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
new file mode 100644
index 000000000..263100b47
--- /dev/null
+++ b/app/helpers/application_helper.rb
@@ -0,0 +1,10 @@
+module ApplicationHelper
+ def simple_time_select_options
+ minutes = %w( 00 15 30 45 )
+ hours = (0..23).to_a.map { |h| format('%.2d', h) }
+ options = hours.map do |h|
+ minutes.map { |m| "#{h}:#{m}" }
+ end.flatten
+ options_for_select(options)
+ end
+end
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
new file mode 100644
index 000000000..a479497df
--- /dev/null
+++ b/app/mailers/application_mailer.rb
@@ -0,0 +1,4 @@
+class ApplicationMailer < ActionMailer::Base
+ default from: 'admin@what.host.should.we.have.here.com'
+ layout 'mailer'
+end
diff --git a/app/mailers/event_invitation_mailer.rb b/app/mailers/event_invitation_mailer.rb
new file mode 100644
index 000000000..06ac6aac5
--- /dev/null
+++ b/app/mailers/event_invitation_mailer.rb
@@ -0,0 +1,10 @@
+class EventInvitationMailer < ApplicationMailer
+ def invite(email_address:, event:)
+ admin_email = 'admin@what.host.should.we.have.here.com'
+ @email_address = email_address
+ @event = event
+ mail(to: email_address,
+ bcc: admin_email,
+ subject: 'Phone call interview')
+ end
+end
diff --git a/app/mailers/reservation_notifier.rb b/app/mailers/reservation_notifier.rb
new file mode 100644
index 000000000..c1da9e1ce
--- /dev/null
+++ b/app/mailers/reservation_notifier.rb
@@ -0,0 +1,10 @@
+class ReservationNotifier < ApplicationMailer
+ def notify(email_address:, reservation:)
+ admin_email = 'admin@what.host.should.we.have.here.com'
+ @email_address = email_address
+ @reservation = reservation
+ mail(to: email_address,
+ bcc: admin_email,
+ subject: 'Interview scheduled')
+ end
+end
diff --git a/app/models/comment.rb b/app/models/comment.rb
index 338adb55a..2c10df8cd 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -16,5 +16,4 @@ class Comment < ActiveRecord::Base
validates_presence_of :content
belongs_to :commentable, polymorphic: true, touch: true
-
end
diff --git a/app/models/v2/event.rb b/app/models/v2/event.rb
new file mode 100644
index 000000000..a21b34c24
--- /dev/null
+++ b/app/models/v2/event.rb
@@ -0,0 +1,6 @@
+class V2::Event < ActiveRecord::Base
+ has_many :time_slots, class_name: '::V2::TimeSlot'
+
+ validates :description, presence: true
+ validates :time_slots, presence: true
+end
diff --git a/app/models/v2/event_invitation.rb b/app/models/v2/event_invitation.rb
new file mode 100644
index 000000000..f99f17d39
--- /dev/null
+++ b/app/models/v2/event_invitation.rb
@@ -0,0 +1,32 @@
+class V2::EventInvitation
+ include ActiveModel::Model
+
+ attr_accessor :email_address, :description, :slot_length, :date, :start_time, :end_time
+ attr_reader :event
+
+ validates :email_address, :description, :slot_length, :date, :start_time, :end_time, presence: true
+
+ def save
+ if valid?
+ @event = V2::Event.new(
+ description: description,
+ time_slots: time_slots
+ )
+
+ @event.save!
+ else
+ false
+ end
+ end
+
+ private
+
+ def time_slots
+ V2::TimeWindow.new(
+ slot_length: slot_length,
+ date: date,
+ start_time: start_time,
+ end_time: end_time
+ ).slots
+ end
+end
diff --git a/app/models/v2/reservation.rb b/app/models/v2/reservation.rb
new file mode 100644
index 000000000..1875956cb
--- /dev/null
+++ b/app/models/v2/reservation.rb
@@ -0,0 +1,9 @@
+class V2::Reservation < ActiveRecord::Base
+ self.table_name = 'v2_reservations'
+
+ belongs_to :time_slot, class_name: '::V2::TimeSlot'
+ belongs_to :person
+
+ validates :person, presence: true
+ validates :time_slot, presence: true
+end
diff --git a/app/models/v2/time_slot.rb b/app/models/v2/time_slot.rb
new file mode 100644
index 000000000..e1b927207
--- /dev/null
+++ b/app/models/v2/time_slot.rb
@@ -0,0 +1,16 @@
+class V2::TimeSlot < ActiveRecord::Base
+ self.table_name = 'v2_time_slots'
+
+ belongs_to :event, class_name: '::V2::Event'
+
+ validates :start_time, presence: true
+ validates :end_time, presence: true
+
+ def to_time_and_weekday
+ "#{start_time.strftime('%H:%M')} - #{end_time.strftime('%H:%M')} #{start_time.strftime('%A %d')}"
+ end
+
+ def to_weekday_and_time
+ "#{start_time.strftime('%A %d')} #{start_time.strftime('%H:%M')} - #{end_time.strftime('%H:%M')}"
+ end
+end
diff --git a/app/models/v2/time_window.rb b/app/models/v2/time_window.rb
new file mode 100644
index 000000000..5cdfbd1a7
--- /dev/null
+++ b/app/models/v2/time_window.rb
@@ -0,0 +1,42 @@
+class V2::TimeWindow
+
+ def initialize(date:, start_time:, end_time:, slot_length:)
+ @date = date
+ @start_time = start_time
+ @end_time = end_time
+ @slot_length = slot_length
+ @slots = []
+ end
+
+ def slots
+ slot_start = start_time
+ slot_end = slot_start + slot_length
+
+ while slot_end <= end_time
+ @slots << ::V2::TimeSlot.new(start_time: slot_start, end_time: slot_end)
+
+ slot_start = slot_end
+ slot_end += slot_length
+ end
+
+ @slots
+ end
+
+ private
+
+ def date
+ Date.strptime(@date, '%m/%d/%Y')
+ end
+
+ def start_time
+ Time.zone.parse("#{date} #{@start_time}")
+ end
+
+ def end_time
+ Time.zone.parse("#{date} #{@end_time}")
+ end
+
+ def slot_length
+ @slot_length.delete(' mins').to_i.minutes
+ end
+end
diff --git a/app/views/event_invitation_mailer/invite.html.erb b/app/views/event_invitation_mailer/invite.html.erb
new file mode 100644
index 000000000..404f8e085
--- /dev/null
+++ b/app/views/event_invitation_mailer/invite.html.erb
@@ -0,0 +1,8 @@
+
Hello, you've been invited to a phone interview
+
+<%= link_to 'Please click to setup a time for your interview',
+ new_v2_reservation_url(
+ email_address: @email_address,
+ event_id: @event.id
+ ) %>
+
diff --git a/app/views/event_invitation_mailer/invite.text.erb b/app/views/event_invitation_mailer/invite.text.erb
new file mode 100644
index 000000000..1abee6281
--- /dev/null
+++ b/app/views/event_invitation_mailer/invite.text.erb
@@ -0,0 +1,7 @@
+Hello, you've been invited to a phone interview
+
+<%= link_to 'Please click to setup a time for your interview',
+ new_v2_reservation_url(
+ email_address: @email_address,
+ event_id: @event.id
+ ) %>
diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb
new file mode 100644
index 000000000..991cf0ffa
--- /dev/null
+++ b/app/views/layouts/mailer.html.erb
@@ -0,0 +1,5 @@
+
+
+ <%= yield %>
+
+
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
new file mode 100644
index 000000000..37f0bddbd
--- /dev/null
+++ b/app/views/layouts/mailer.text.erb
@@ -0,0 +1 @@
+<%= yield %>
diff --git a/app/views/reservation_notifier/notify.html.erb b/app/views/reservation_notifier/notify.html.erb
new file mode 100644
index 000000000..a427697c7
--- /dev/null
+++ b/app/views/reservation_notifier/notify.html.erb
@@ -0,0 +1 @@
+An interview has been booked for <%= @reservation.time_slot.to_weekday_and_time %>
diff --git a/app/views/reservation_notifier/notify.text.erb b/app/views/reservation_notifier/notify.text.erb
new file mode 100644
index 000000000..02ac3b815
--- /dev/null
+++ b/app/views/reservation_notifier/notify.text.erb
@@ -0,0 +1 @@
+An interview has been booked for <%= @reservation.time_slot.to_weekday_and_time %>
\ No newline at end of file
diff --git a/app/views/v2/event_invitations/_form.html.erb b/app/views/v2/event_invitations/_form.html.erb
new file mode 100644
index 000000000..8e836cdde
--- /dev/null
+++ b/app/views/v2/event_invitations/_form.html.erb
@@ -0,0 +1,48 @@
+<%= form_for @event_invitation, url: v2_event_invitations_path, html: { class: 'form-horizontal' } do |f| %>
+
+ <%= f.label :email_address, "Person's email address", :class => 'control-label' %>
+
+ <%= f.text_field :email_address, :class => 'text_field' %>
+
+
+
+ <%= f.label :description, 'Event description', :class => 'control-label' %>
+
+ <%= f.text_area :description, :class => 'text_area' %>
+
+
+
+ <%= f.label :slot_length, 'Call length', :class => 'control-label' %>
+
+ <%= f.select :slot_length, options_for_select([15,30,45,60].map {|o| "#{o.to_s} mins"}), :class => 'text_field' %>
+
+
+
+ Time window
+
+ <%= f.label :date, :class => 'control-label' %>
+
+ <%= f.text_field :date, :class => 'text_field' %>
+
+
+
+
+ <%= f.label :start_time, :class => 'control-label' %>
+
+ <%= f.select :start_time, simple_time_select_options, :class => 'text_field' %>
+
+
+
+
+ <%= f.label :end_time, :class => 'control-label' %>
+
+ <%= f.select :end_time, simple_time_select_options, :class => 'text_field' %>
+
+
+
+
+ <%= f.submit 'Send invitation', :class => 'btn btn-primary' %>
+ <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
+ people_path, :class => 'btn' %>
+
+<% end %>
diff --git a/app/views/v2/event_invitations/new.html.erb b/app/views/v2/event_invitations/new.html.erb
new file mode 100644
index 000000000..57e1bdf6c
--- /dev/null
+++ b/app/views/v2/event_invitations/new.html.erb
@@ -0,0 +1,4 @@
+Invite a person to a phone call
+
+<%= render 'form' %>
+
diff --git a/app/views/v2/reservations/_form.html.erb b/app/views/v2/reservations/_form.html.erb
new file mode 100644
index 000000000..d1e712f60
--- /dev/null
+++ b/app/views/v2/reservations/_form.html.erb
@@ -0,0 +1,13 @@
+<%= form_for @reservation, url: v2_reservations_path, html: { class: 'form-horizontal' } do |form| %>
+ <%= form.hidden_field :person_id, value: @person.id.to_s %>
+
+ <% @time_slots.each do |time_slot| %>
+
+ <%= form.label :time_slot_id, time_slot.to_time_and_weekday, :class => 'control-label' %>
+ <%= form.radio_button :time_slot_id, time_slot.id, :class => 'radio_button' %>
+
+ <% end %>
+
+ <%= form.submit "Confirm reservation" %>
+
+<% end %>
diff --git a/app/views/v2/reservations/new.html.erb b/app/views/v2/reservations/new.html.erb
new file mode 100644
index 000000000..dfc985b36
--- /dev/null
+++ b/app/views/v2/reservations/new.html.erb
@@ -0,0 +1,4 @@
+Please reserve a time for the call
+
+<%= render 'form' %>
+
diff --git a/config/application.rb b/config/application.rb
index 21991850f..9c0812e1d 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -20,7 +20,7 @@ class Application < Rails::Application
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
- config.autoload_paths += %W(#{config.root}/app/jobs)
+ config.autoload_paths += %W(#{config.root}/app/jobs #{config.root}/app/mailers)
# Analytics
Logan::Application.config.google_analytics_enabled = false
diff --git a/config/routes.rb b/config/routes.rb
index 8c6244df0..74887ab49 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,11 @@
resources :people, only: [:new, :create]
end
+ namespace :v2 do
+ resources :event_invitations, only: [:new, :create]
+ resources :reservations, only: [:new, :create]
+ end
+
get 'registration', to: 'public/people#new'
resources :twilio_wufoos
diff --git a/db/migrate/20160208135550_add_v2_event.rb b/db/migrate/20160208135550_add_v2_event.rb
new file mode 100644
index 000000000..97f2b6c49
--- /dev/null
+++ b/db/migrate/20160208135550_add_v2_event.rb
@@ -0,0 +1,8 @@
+class AddV2Event < ActiveRecord::Migration
+ def change
+ create_table :v2_events do |t|
+ t.integer :user_id
+ t.string :description
+ end
+ end
+end
diff --git a/db/migrate/20160210142609_create_v2_time_slots.rb b/db/migrate/20160210142609_create_v2_time_slots.rb
new file mode 100644
index 000000000..e60719377
--- /dev/null
+++ b/db/migrate/20160210142609_create_v2_time_slots.rb
@@ -0,0 +1,9 @@
+class CreateV2TimeSlots < ActiveRecord::Migration
+ def change
+ create_table :v2_time_slots do |t|
+ t.integer :event_id
+ t.datetime :start_time
+ t.datetime :end_time
+ end
+ end
+end
diff --git a/db/migrate/20160214152610_create_v2_reservations.rb b/db/migrate/20160214152610_create_v2_reservations.rb
new file mode 100644
index 000000000..3f1387286
--- /dev/null
+++ b/db/migrate/20160214152610_create_v2_reservations.rb
@@ -0,0 +1,8 @@
+class CreateV2Reservations < ActiveRecord::Migration
+ def change
+ create_table :v2_reservations do |t|
+ t.integer :time_slot_id
+ t.integer :person_id
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 41f6efa1c..8d2bebb94 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,199 +11,216 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20151019004127) do
- create_table 'applications', force: :cascade do |t|
- t.string 'name', limit: 255
- t.text 'description', limit: 65535
- t.string 'url', limit: 255
- t.string 'source_url', limit: 255
- t.string 'creator_name', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'program_id', limit: 4
- t.integer 'created_by', limit: 4
- t.integer 'updated_by', limit: 4
- end
-
- create_table 'comments', force: :cascade do |t|
- t.text 'content', limit: 65535
- t.integer 'user_id', limit: 4
- t.string 'commentable_type', limit: 255
- t.integer 'commentable_id', limit: 4
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'created_by', limit: 4
- end
-
- create_table 'delayed_jobs', force: :cascade do |t|
- t.integer 'priority', limit: 4, default: 0, null: false
- t.integer 'attempts', limit: 4, default: 0, null: false
- t.text 'handler', limit: 65535, null: false
- t.text 'last_error', limit: 65535
- t.datetime 'run_at'
- t.datetime 'locked_at'
- t.datetime 'failed_at'
- t.string 'locked_by', limit: 255
- t.string 'queue', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'delayed_reference_id', limit: 4
- t.string 'delayed_reference_type', limit: 255
- end
-
- add_index 'delayed_jobs', ['delayed_reference_type'], name: 'delayed_jobs_delayed_reference_type', using: :btree
- add_index 'delayed_jobs', %w(priority run_at), name: 'delayed_jobs_priority', using: :btree
- add_index 'delayed_jobs', ['queue'], name: 'delayed_jobs_queue', using: :btree
-
- create_table 'events', force: :cascade do |t|
- t.string 'name', limit: 255
- t.text 'description', limit: 65535
- t.datetime 'starts_at'
- t.datetime 'ends_at'
- t.text 'location', limit: 65535
- t.text 'address', limit: 65535
- t.integer 'capacity', limit: 4
- t.integer 'application_id', limit: 4
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'created_by', limit: 4
- t.integer 'updated_by', limit: 4
- end
-
- create_table 'mailchimp_exports', force: :cascade do |t|
- t.string 'name', limit: 255
- t.text 'body', limit: 65535
- t.integer 'created_by', limit: 4
- t.datetime 'created_at'
- t.datetime 'updated_at'
- end
-
- create_table 'people', force: :cascade do |t|
- t.string 'first_name', limit: 255
- t.string 'last_name', limit: 255
- t.string 'email_address', limit: 255
- t.string 'address_1', limit: 255
- t.string 'address_2', limit: 255
- t.string 'city', limit: 255
- t.string 'state', limit: 255
- t.string 'postal_code', limit: 255
- t.integer 'geography_id', limit: 4
- t.integer 'primary_device_id', limit: 4
- t.string 'primary_device_description', limit: 255
- t.integer 'secondary_device_id', limit: 4
- t.string 'secondary_device_description', limit: 255
- t.integer 'primary_connection_id', limit: 4
- t.string 'primary_connection_description', limit: 255
- t.string 'phone_number', limit: 255
- t.string 'participation_type', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.string 'signup_ip', limit: 255
- t.datetime 'signup_at'
- t.string 'voted', limit: 255
- t.string 'called_311', limit: 255
- t.integer 'secondary_connection_id', limit: 4
- t.string 'secondary_connection_description', limit: 255
- t.string 'verified', limit: 255
- t.string 'preferred_contact_method', limit: 255
- end
-
- create_table 'programs', force: :cascade do |t|
- t.string 'name', limit: 255
- t.text 'description', limit: 65535
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'created_by', limit: 4
- t.integer 'updated_by', limit: 4
- end
-
- create_table 'reservations', force: :cascade do |t|
- t.integer 'person_id', limit: 4
- t.integer 'event_id', limit: 4
- t.datetime 'confirmed_at'
- t.integer 'created_by', limit: 4
- t.datetime 'attended_at'
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'updated_by', limit: 4
- end
-
- create_table 'submissions', force: :cascade do |t|
- t.text 'raw_content', limit: 65535
- t.integer 'person_id', limit: 4
- t.string 'ip_addr', limit: 255
- t.string 'entry_id', limit: 255
- t.text 'form_structure', limit: 65535
- t.text 'field_structure', limit: 65535
- t.datetime 'created_at'
- t.datetime 'updated_at'
- end
-
- create_table 'taggings', force: :cascade do |t|
- t.string 'taggable_type', limit: 255
- t.integer 'taggable_id', limit: 4
- t.integer 'created_by', limit: 4
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.integer 'tag_id', limit: 4
- end
-
- create_table 'tags', force: :cascade do |t|
- t.string 'name', limit: 255
- t.integer 'created_by', limit: 4
- t.datetime 'created_at'
- t.datetime 'updated_at'
- end
-
- create_table 'twilio_messages', force: :cascade do |t|
- t.string 'message_sid', limit: 255
- t.datetime 'date_created'
- t.datetime 'date_updated'
- t.datetime 'date_sent'
- t.string 'account_sid', limit: 255
- t.string 'from', limit: 255
- t.string 'to', limit: 255
- t.string 'body', limit: 255
- t.string 'status', limit: 255
- t.string 'error_code', limit: 255
- t.string 'error_message', limit: 255
- t.string 'direction', limit: 255
- t.string 'from_city', limit: 255
- t.string 'from_state', limit: 255
- t.string 'from_zip', limit: 255
- t.string 'wufoo_formid', limit: 255
- t.integer 'conversation_count', limit: 4
- t.string 'signup_verify', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- end
-
- create_table 'twilio_wufoos', force: :cascade do |t|
- t.string 'name', limit: 255
- t.string 'wufoo_formid', limit: 255
- t.string 'twilio_keyword', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.boolean 'status', default: false, null: false
- t.string 'end_message', limit: 255
- t.string 'form_type', limit: 255
- end
-
- create_table 'users', force: :cascade do |t|
- t.string 'email', limit: 255, default: '', null: false
- t.string 'encrypted_password', limit: 255, default: '', null: false
- t.string 'reset_password_token', limit: 255
- t.datetime 'reset_password_sent_at'
- t.datetime 'remember_created_at'
- t.integer 'sign_in_count', limit: 4, default: 0
- t.datetime 'current_sign_in_at'
- t.datetime 'last_sign_in_at'
- t.string 'current_sign_in_ip', limit: 255
- t.string 'last_sign_in_ip', limit: 255
- t.string 'password_salt', limit: 255
- t.string 'invitation_token', limit: 255
- t.datetime 'created_at'
- t.datetime 'updated_at'
- t.boolean 'approved', default: false, null: false
+ActiveRecord::Schema.define(version: 20160214152610) do
+
+ create_table "applications", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.text "description", limit: 65535
+ t.string "url", limit: 255
+ t.string "source_url", limit: 255
+ t.string "creator_name", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "program_id", limit: 4
+ t.integer "created_by", limit: 4
+ t.integer "updated_by", limit: 4
+ end
+
+ create_table "comments", force: :cascade do |t|
+ t.text "content", limit: 65535
+ t.integer "user_id", limit: 4
+ t.string "commentable_type", limit: 255
+ t.integer "commentable_id", limit: 4
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "created_by", limit: 4
+ end
+
+ create_table "delayed_jobs", force: :cascade do |t|
+ t.integer "priority", limit: 4, default: 0, null: false
+ t.integer "attempts", limit: 4, default: 0, null: false
+ t.text "handler", limit: 65535, null: false
+ t.text "last_error", limit: 65535
+ t.datetime "run_at"
+ t.datetime "locked_at"
+ t.datetime "failed_at"
+ t.string "locked_by", limit: 255
+ t.string "queue", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "delayed_reference_id", limit: 4
+ t.string "delayed_reference_type", limit: 255
+ end
+
+ add_index "delayed_jobs", ["delayed_reference_type"], name: "delayed_jobs_delayed_reference_type", using: :btree
+ add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
+ add_index "delayed_jobs", ["queue"], name: "delayed_jobs_queue", using: :btree
+
+ create_table "events", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.text "description", limit: 65535
+ t.datetime "starts_at"
+ t.datetime "ends_at"
+ t.text "location", limit: 65535
+ t.text "address", limit: 65535
+ t.integer "capacity", limit: 4
+ t.integer "application_id", limit: 4
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "created_by", limit: 4
+ t.integer "updated_by", limit: 4
+ end
+
+ create_table "mailchimp_exports", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.text "body", limit: 65535
+ t.integer "created_by", limit: 4
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "people", force: :cascade do |t|
+ t.string "first_name", limit: 255
+ t.string "last_name", limit: 255
+ t.string "email_address", limit: 255
+ t.string "address_1", limit: 255
+ t.string "address_2", limit: 255
+ t.string "city", limit: 255
+ t.string "state", limit: 255
+ t.string "postal_code", limit: 255
+ t.integer "geography_id", limit: 4
+ t.integer "primary_device_id", limit: 4
+ t.string "primary_device_description", limit: 255
+ t.integer "secondary_device_id", limit: 4
+ t.string "secondary_device_description", limit: 255
+ t.integer "primary_connection_id", limit: 4
+ t.string "primary_connection_description", limit: 255
+ t.string "phone_number", limit: 255
+ t.string "participation_type", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.string "signup_ip", limit: 255
+ t.datetime "signup_at"
+ t.string "voted", limit: 255
+ t.string "called_311", limit: 255
+ t.integer "secondary_connection_id", limit: 4
+ t.string "secondary_connection_description", limit: 255
+ t.string "verified", limit: 255
+ t.string "preferred_contact_method", limit: 255
+ end
+
+ create_table "programs", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.text "description", limit: 65535
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "created_by", limit: 4
+ t.integer "updated_by", limit: 4
+ end
+
+ create_table "reservations", force: :cascade do |t|
+ t.integer "person_id", limit: 4
+ t.integer "event_id", limit: 4
+ t.datetime "confirmed_at"
+ t.integer "created_by", limit: 4
+ t.datetime "attended_at"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "updated_by", limit: 4
+ end
+
+ create_table "submissions", force: :cascade do |t|
+ t.text "raw_content", limit: 65535
+ t.integer "person_id", limit: 4
+ t.string "ip_addr", limit: 255
+ t.string "entry_id", limit: 255
+ t.text "form_structure", limit: 65535
+ t.text "field_structure", limit: 65535
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
+
+ create_table "taggings", force: :cascade do |t|
+ t.string "taggable_type", limit: 255
+ t.integer "taggable_id", limit: 4
+ t.integer "created_by", limit: 4
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "tag_id", limit: 4
+ end
+
+ create_table "tags", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.integer "created_by", limit: 4
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "twilio_messages", force: :cascade do |t|
+ t.string "message_sid", limit: 255
+ t.datetime "date_created"
+ t.datetime "date_updated"
+ t.datetime "date_sent"
+ t.string "account_sid", limit: 255
+ t.string "from", limit: 255
+ t.string "to", limit: 255
+ t.string "body", limit: 255
+ t.string "status", limit: 255
+ t.string "error_code", limit: 255
+ t.string "error_message", limit: 255
+ t.string "direction", limit: 255
+ t.string "from_city", limit: 255
+ t.string "from_state", limit: 255
+ t.string "from_zip", limit: 255
+ t.string "wufoo_formid", limit: 255
+ t.integer "conversation_count", limit: 4
+ t.string "signup_verify", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "twilio_wufoos", force: :cascade do |t|
+ t.string "name", limit: 255
+ t.string "wufoo_formid", limit: 255
+ t.string "twilio_keyword", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.boolean "status", default: false, null: false
+ t.string "end_message", limit: 255
+ t.string "form_type", limit: 255
+ end
+
+ create_table "users", force: :cascade do |t|
+ t.string "email", limit: 255, default: "", null: false
+ t.string "encrypted_password", limit: 255, default: "", null: false
+ t.string "reset_password_token", limit: 255
+ t.datetime "reset_password_sent_at"
+ t.datetime "remember_created_at"
+ t.integer "sign_in_count", limit: 4, default: 0
+ t.datetime "current_sign_in_at"
+ t.datetime "last_sign_in_at"
+ t.string "current_sign_in_ip", limit: 255
+ t.string "last_sign_in_ip", limit: 255
+ t.string "password_salt", limit: 255
+ t.string "invitation_token", limit: 255
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.boolean "approved", default: false, null: false
+ end
+
+ create_table "v2_events", force: :cascade do |t|
+ t.integer "user_id", limit: 4
+ end
+
+ create_table "v2_reservations", force: :cascade do |t|
+ t.integer "time_slot_id", limit: 4
+ t.integer "person_id", limit: 4
+ end
+
+ create_table "v2_time_slots", force: :cascade do |t|
+ t.integer "event_id", limit: 4
+ t.datetime "start_time"
+ t.datetime "end_time"
+ end
+
end
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
new file mode 100644
index 000000000..ffe01d8c6
--- /dev/null
+++ b/spec/factories/events.rb
@@ -0,0 +1,12 @@
+require 'faker'
+
+FactoryGirl.define do
+ factory :event, class: V2::Event do
+ description 'Lorem ipsum for now'
+
+ before(:create) do |event|
+ # create_list(:time_slot, 3, event: event)
+ 3.times { event.time_slots << FactoryGirl.create(:time_slot) }
+ end
+ end
+end
diff --git a/spec/factories/people.rb b/spec/factories/people.rb
index d0e1025b5..24431be8a 100644
--- a/spec/factories/people.rb
+++ b/spec/factories/people.rb
@@ -7,7 +7,7 @@
factory :person do
first_name Faker::Name.first_name
last_name Faker::Name.last_name
- email_address Faker::Internet.email
+ email_address { Faker::Internet.email }
phone_number Faker::PhoneNumber.phone_number
address_1 Faker::Address.street_address
address_2 Faker::Address.secondary_address
diff --git a/spec/factories/time_slots.rb b/spec/factories/time_slots.rb
new file mode 100644
index 000000000..e8724c63c
--- /dev/null
+++ b/spec/factories/time_slots.rb
@@ -0,0 +1,8 @@
+require 'faker'
+
+FactoryGirl.define do
+ factory :time_slot, class: V2::TimeSlot do
+ sequence(:start_time) { |i| Faker::Time.forward(i.day, :morning) }
+ end_time { start_time + 30.minutes }
+ end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
new file mode 100644
index 000000000..58d59c4a9
--- /dev/null
+++ b/spec/factories/users.rb
@@ -0,0 +1,10 @@
+require 'faker'
+
+FactoryGirl.define do
+ factory :user do
+ email { Faker::Internet.email }
+ password 'password'
+ password_confirmation 'password'
+ approved true
+ end
+end
diff --git a/spec/features/invite_person_to_phone_call_spec.rb b/spec/features/invite_person_to_phone_call_spec.rb
new file mode 100644
index 000000000..9779e0212
--- /dev/null
+++ b/spec/features/invite_person_to_phone_call_spec.rb
@@ -0,0 +1,64 @@
+require 'rails_helper'
+require 'faker'
+require 'capybara/email/rspec'
+
+feature 'Invite a person to a phone call' do
+ scenario 'with valid data' do
+ login_with_admin_user
+
+ visit '/v2/event_invitations/new'
+
+ research_subject_email = 'person@test.com.br'
+ admin_email = 'admin@what.host.should.we.have.here.com'
+
+ fill_in "Person's email address", with: research_subject_email
+
+ # TODO: allow to fill in multiple email addresses once basic invitation works
+
+ fill_in 'Event description', with: "We're looking for mothers between the age of 16-26 for a phone interview"
+
+ select '30 mins', from: 'Call length'
+
+ fill_in 'Date', with: '02/02/2016'
+ select '12:00', from: 'Start time'
+ select '15:30', from: 'End time'
+
+ # TODO: implement multiple time windows after invitation for single time window works
+ #
+ # click_link 'Add another time window'
+ #
+ # fill_in 'Date', with: '02/03/2016'
+ # select '12:00', from: 'Start time'
+ # select '14:30', from: 'End time'
+
+ click_button 'Send invitation'
+
+ expect(page).to have_text 'Person was successfully invited.'
+
+ [research_subject_email, admin_email].each do |email_address|
+ open_email(email_address)
+
+ # TODO: substitute placeholder text
+ expect(current_email).
+ to have_content "Hello, you've been invited to a phone interview"
+ end
+ end
+
+ scenario 'with invalid data' do
+ login_with_admin_user
+
+ visit '/v2/event_invitations/new'
+
+ click_button 'Send invitation'
+
+ expect(page).to have_text('There were problems with some of the fields.')
+ end
+end
+
+def login_with_admin_user
+ user = FactoryGirl.create(:user)
+ visit '/users/sign_in'
+ fill_in 'Email', with: user.email
+ fill_in 'Password', with: user.password
+ click_button 'Sign in'
+end
diff --git a/spec/features/person_responds_to_interview_invitation_spec.rb b/spec/features/person_responds_to_interview_invitation_spec.rb
new file mode 100644
index 000000000..63beb79ea
--- /dev/null
+++ b/spec/features/person_responds_to_interview_invitation_spec.rb
@@ -0,0 +1,47 @@
+require 'rails_helper'
+require 'capybara/email/rspec'
+
+feature 'Person responds to interview invitation' do
+ before do
+ clear_emails
+ @event = FactoryGirl.create(:event)
+ @person = FactoryGirl.create(:person)
+
+ EventInvitationMailer.invite(email_address: @person.email_address, event: @event).deliver_now
+
+ open_email(@person.email_address)
+
+ current_email.click_link 'Please click to setup a time for your interview'
+ end
+
+ scenario 'over email, successfully' do
+ @event.time_slots.each do |time|
+ expect(page).to have_content time.to_time_and_weekday
+ end
+
+ find('#v2_reservation_time_slot_id_1').set(true)
+
+ click_button 'Confirm reservation'
+
+ selected_time = @event.time_slots.first.to_weekday_and_time
+
+ expect(page).to have_content "An interview has been booked for #{selected_time}"
+
+ admin_email = 'admin@what.host.should.we.have.here.com'
+ research_subject_email = @person.email_address
+
+ [admin_email, research_subject_email].each do |email_address|
+ open_email(email_address)
+
+ # TODO: substitute placeholder text
+ expect(current_email).
+ to have_content "An interview has been booked for #{selected_time}"
+ end
+ end
+
+ scenario 'over email, but forgetting to select a time' do
+ click_button 'Confirm reservation'
+
+ expect(page).to have_content "No time slot was selected, couldn't create the reservation"
+ end
+end
diff --git a/spec/mailers/event_invitation_mailer_spec.rb b/spec/mailers/event_invitation_mailer_spec.rb
new file mode 100644
index 000000000..93779e207
--- /dev/null
+++ b/spec/mailers/event_invitation_mailer_spec.rb
@@ -0,0 +1,4 @@
+require 'rails_helper'
+
+describe EventInvitationMailer, type: :mailer do
+end
diff --git a/spec/mailers/previews/event_invitation_mailer_preview.rb b/spec/mailers/previews/event_invitation_mailer_preview.rb
new file mode 100644
index 000000000..bcfbc7998
--- /dev/null
+++ b/spec/mailers/previews/event_invitation_mailer_preview.rb
@@ -0,0 +1,4 @@
+# Preview all emails at http://localhost:3000/rails/mailers/event_invitation_mailer
+class EventInvitationMailerPreview < ActionMailer::Preview
+
+end
diff --git a/spec/models/v2/event_invitation_spec.rb b/spec/models/v2/event_invitation_spec.rb
new file mode 100644
index 000000000..0d9fb52a0
--- /dev/null
+++ b/spec/models/v2/event_invitation_spec.rb
@@ -0,0 +1,41 @@
+require 'rails_helper'
+
+describe V2::EventInvitation do
+ it { is_expected.to validate_presence_of(:email_address) }
+ it { is_expected.to validate_presence_of(:description) }
+ it { is_expected.to validate_presence_of(:slot_length) }
+ it { is_expected.to validate_presence_of(:date) }
+ it { is_expected.to validate_presence_of(:start_time) }
+ it { is_expected.to validate_presence_of(:end_time) }
+
+ describe '#save' do
+ describe 'when valid' do
+ let(:args) do
+ {
+ email_address: 'some@email.com',
+ description: 'lorem',
+ slot_length: '45 mins',
+ date: '03/20/2016',
+ start_time: '15:00',
+ end_time: '16:30'
+ }
+ end
+
+ subject { described_class.new(args) }
+
+ it 'creates a new event' do
+ expect { subject.save }.to change { V2::Event.count }.from(0).to(1)
+ end
+
+ it 'creates a new time slots' do
+ expect { subject.save }.to change { V2::TimeSlot.count }.from(0).to(2)
+ end
+ end
+
+ describe 'with missing data' do
+ it 'returns false' do
+ expect(subject.save).to eql false
+ end
+ end
+ end
+end
diff --git a/spec/models/v2/event_spec.rb b/spec/models/v2/event_spec.rb
new file mode 100644
index 000000000..3eec5c55a
--- /dev/null
+++ b/spec/models/v2/event_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+
+describe V2::Event do
+ it { is_expected.to validate_presence_of(:description) }
+ it { is_expected.to validate_presence_of(:time_slots) }
+end
diff --git a/spec/models/v2/reservation_spec.rb b/spec/models/v2/reservation_spec.rb
new file mode 100644
index 000000000..52528aded
--- /dev/null
+++ b/spec/models/v2/reservation_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+
+describe V2::Reservation do
+ it { is_expected.to validate_presence_of(:person) }
+ it { is_expected.to validate_presence_of(:time_slot) }
+end
diff --git a/spec/models/v2/time_slot_spec.rb b/spec/models/v2/time_slot_spec.rb
new file mode 100644
index 000000000..76fe80460
--- /dev/null
+++ b/spec/models/v2/time_slot_spec.rb
@@ -0,0 +1,6 @@
+require 'rails_helper'
+
+describe V2::TimeSlot do
+ it { is_expected.to validate_presence_of(:start_time) }
+ it { is_expected.to validate_presence_of(:end_time) }
+end
diff --git a/spec/models/v2/time_window_spec.rb b/spec/models/v2/time_window_spec.rb
new file mode 100644
index 000000000..18d1c60cd
--- /dev/null
+++ b/spec/models/v2/time_window_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+describe V2::TimeWindow do
+ describe '#slots' do
+ subject do
+ described_class.new(
+ date: '06/12/2016',
+ start_time: '09:30',
+ end_time: '10:30',
+ slot_length: '30 mins'
+ ).slots
+ end
+
+ it 'returns 2 time slots' do
+ expect(subject.count).to eql 2
+ end
+
+ it 'returns a time slot from 09:30 to 10:00' do
+ expect(
+ subject.find do |time_slot|
+ time_slot.start_time == Time.zone.parse('12/06/2016 09:30') &&
+ time_slot.end_time == Time.zone.parse('12/06/2016 10:00')
+ end
+ ).to_not be_nil
+ end
+
+ it 'returns a time slot from 10:00 to 10:30' do
+ expect(
+ subject.find do |time_slot|
+ time_slot.start_time == Time.zone.parse('12/06/2016 10:00') &&
+ time_slot.end_time == Time.zone.parse('12/06/2016 10:30')
+ end
+ ).to_not be_nil
+ end
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 2a70a15c3..853a53396 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -4,6 +4,7 @@
require 'rspec/rails'
require 'spec_helper'
require 'shoulda/matchers'
+require 'database_cleaner'
ActiveRecord::Migration.maintain_test_schema!
@@ -18,17 +19,27 @@
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/test/fixtures"
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
- # examples within a transaction, remove the following line or assign false
- # instead of true.
- config.use_transactional_fixtures = true
+ config.use_transactional_fixtures = false
- # The different available types are documented in the features, such as in
- # https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
- # Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
- # arbitrary gems may also be filtered via:
- # config.filter_gems_from_backtrace("gem name")
+
+ config.use_transactional_fixtures = false
+
+ config.before(:suite) do
+ DatabaseCleaner.clean_with(:truncation)
+ end
+
+ config.before(:each) do
+ DatabaseCleaner.strategy = :transaction
+ end
+
+ config.before(:each) do
+ DatabaseCleaner.start
+ end
+
+ config.append_after(:each) do
+ DatabaseCleaner.clean
+ end
end