saasy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +114 -0
- data/Gemfile +26 -0
- data/README.md +118 -0
- data/Rakefile +38 -0
- data/app/controllers/accounts_controller.rb +68 -0
- data/app/controllers/billings_controller.rb +25 -0
- data/app/controllers/invitations_controller.rb +65 -0
- data/app/controllers/memberships_controller.rb +45 -0
- data/app/controllers/plans_controller.rb +24 -0
- data/app/controllers/profiles_controller.rb +19 -0
- data/app/helpers/limits_helper.rb +13 -0
- data/app/mailers/billing_mailer.rb +53 -0
- data/app/mailers/invitation_mailer.rb +18 -0
- data/app/models/invitation.rb +113 -0
- data/app/models/limit.rb +49 -0
- data/app/models/membership.rb +26 -0
- data/app/models/permission.rb +19 -0
- data/app/models/signup.rb +163 -0
- data/app/views/accounts/_account.html.erb +9 -0
- data/app/views/accounts/_blank_slate.html.erb +6 -0
- data/app/views/accounts/_projects.html.erb +12 -0
- data/app/views/accounts/_subnav.html.erb +10 -0
- data/app/views/accounts/edit.html.erb +34 -0
- data/app/views/accounts/index.html.erb +9 -0
- data/app/views/accounts/new.html.erb +36 -0
- data/app/views/billing_mailer/completed_trial.text.erb +13 -0
- data/app/views/billing_mailer/expiring_trial.text.erb +15 -0
- data/app/views/billing_mailer/new_unactivated.text.erb +1 -0
- data/app/views/billing_mailer/problem.html.erb +13 -0
- data/app/views/billing_mailer/problem.text.erb +14 -0
- data/app/views/billing_mailer/receipt.html.erb +41 -0
- data/app/views/billing_mailer/receipt.text.erb +25 -0
- data/app/views/billings/_form.html.erb +8 -0
- data/app/views/billings/edit.html.erb +13 -0
- data/app/views/billings/show.html.erb +29 -0
- data/app/views/invitation_mailer/invitation.text.erb +6 -0
- data/app/views/invitations/new.html.erb +17 -0
- data/app/views/invitations/show.html.erb +22 -0
- data/app/views/layouts/saucy.html.erb +36 -0
- data/app/views/limits/_meter.html.erb +13 -0
- data/app/views/memberships/edit.html.erb +21 -0
- data/app/views/memberships/index.html.erb +17 -0
- data/app/views/plans/_plan.html.erb +32 -0
- data/app/views/plans/_terms.html.erb +15 -0
- data/app/views/plans/edit.html.erb +33 -0
- data/app/views/plans/index.html.erb +12 -0
- data/app/views/profiles/_inputs.html.erb +5 -0
- data/app/views/profiles/edit.html.erb +36 -0
- data/app/views/projects/_form.html.erb +36 -0
- data/app/views/projects/edit.html.erb +22 -0
- data/app/views/projects/index.html.erb +28 -0
- data/app/views/projects/new.html.erb +13 -0
- data/app/views/projects/show.html.erb +0 -0
- data/app/views/shared/_project_dropdown.html.erb +55 -0
- data/app/views/shared/_saucy_javascript.html.erb +33 -0
- data/config/locales/en.yml +37 -0
- data/config/routes.rb +19 -0
- data/features/run_features.feature +83 -0
- data/features/step_definitions/clearance_steps.rb +45 -0
- data/features/step_definitions/rails_steps.rb +73 -0
- data/features/step_definitions/saucy_steps.rb +8 -0
- data/features/support/env.rb +4 -0
- data/features/support/file.rb +11 -0
- data/lib/generators/saucy/base.rb +18 -0
- data/lib/generators/saucy/features/features_generator.rb +91 -0
- data/lib/generators/saucy/features/templates/README +3 -0
- data/lib/generators/saucy/features/templates/factories.rb +71 -0
- data/lib/generators/saucy/features/templates/features/edit_profile.feature +9 -0
- data/lib/generators/saucy/features/templates/features/edit_project_permissions.feature +37 -0
- data/lib/generators/saucy/features/templates/features/edit_user_permissions.feature +47 -0
- data/lib/generators/saucy/features/templates/features/manage_account.feature +35 -0
- data/lib/generators/saucy/features/templates/features/manage_billing.feature +93 -0
- data/lib/generators/saucy/features/templates/features/manage_plan.feature +143 -0
- data/lib/generators/saucy/features/templates/features/manage_projects.feature +139 -0
- data/lib/generators/saucy/features/templates/features/manage_users.feature +142 -0
- data/lib/generators/saucy/features/templates/features/new_account.feature +33 -0
- data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
- data/lib/generators/saucy/features/templates/features/sign_up.feature +32 -0
- data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +65 -0
- data/lib/generators/saucy/features/templates/features/trial_plans.feature +82 -0
- data/lib/generators/saucy/features/templates/step_definitions/account_steps.rb +30 -0
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +25 -0
- data/lib/generators/saucy/features/templates/step_definitions/cron_steps.rb +23 -0
- data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +40 -0
- data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
- data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +51 -0
- data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +16 -0
- data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
- data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +37 -0
- data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +100 -0
- data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/install/install_generator.rb +40 -0
- data/lib/generators/saucy/install/templates/controllers/projects_controller.rb +3 -0
- data/lib/generators/saucy/install/templates/create_saucy_tables.rb +115 -0
- data/lib/generators/saucy/install/templates/models/account.rb +3 -0
- data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
- data/lib/generators/saucy/install/templates/models/project.rb +3 -0
- data/lib/generators/saucy/specs/specs_generator.rb +20 -0
- data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/views/views_generator.rb +23 -0
- data/lib/saucy.rb +10 -0
- data/lib/saucy/account.rb +132 -0
- data/lib/saucy/account_authorization.rb +67 -0
- data/lib/saucy/configuration.rb +29 -0
- data/lib/saucy/engine.rb +35 -0
- data/lib/saucy/fake_braintree.rb +134 -0
- data/lib/saucy/layouts.rb +36 -0
- data/lib/saucy/plan.rb +54 -0
- data/lib/saucy/project.rb +125 -0
- data/lib/saucy/projects_controller.rb +94 -0
- data/lib/saucy/railties/tasks.rake +28 -0
- data/lib/saucy/routing_extensions.rb +121 -0
- data/lib/saucy/subscription.rb +237 -0
- data/lib/saucy/user.rb +30 -0
- data/spec/controllers/accounts_controller_spec.rb +228 -0
- data/spec/controllers/application_controller_spec.rb +32 -0
- data/spec/controllers/invitations_controller_spec.rb +215 -0
- data/spec/controllers/memberships_controller_spec.rb +117 -0
- data/spec/controllers/plans_controller_spec.rb +13 -0
- data/spec/controllers/profiles_controller_spec.rb +48 -0
- data/spec/controllers/projects_controller_spec.rb +216 -0
- data/spec/environment.rb +95 -0
- data/spec/layouts_spec.rb +21 -0
- data/spec/mailers/billing_mailer_spec.rb +68 -0
- data/spec/mailers/invitiation_mailer_spec.rb +19 -0
- data/spec/models/account_spec.rb +218 -0
- data/spec/models/invitation_spec.rb +320 -0
- data/spec/models/limit_spec.rb +70 -0
- data/spec/models/membership_spec.rb +37 -0
- data/spec/models/permission_spec.rb +30 -0
- data/spec/models/plan_spec.rb +81 -0
- data/spec/models/project_spec.rb +223 -0
- data/spec/models/signup_spec.rb +177 -0
- data/spec/models/subscription_spec.rb +481 -0
- data/spec/models/user_spec.rb +72 -0
- data/spec/route_extensions_spec.rb +51 -0
- data/spec/saucy_spec.rb +62 -0
- data/spec/scaffold/config/routes.rb +5 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/authentication_helpers.rb +81 -0
- data/spec/support/authorization_helpers.rb +56 -0
- data/spec/support/braintree.rb +7 -0
- data/spec/support/clearance_matchers.rb +55 -0
- data/spec/support/notifications.rb +57 -0
- data/spec/views/accounts/_account.html.erb_spec.rb +37 -0
- metadata +325 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe User, "valid" do
|
4
|
+
subject { Factory(:user) }
|
5
|
+
|
6
|
+
it { should validate_presence_of(:name) }
|
7
|
+
|
8
|
+
it { should have_many(:memberships) }
|
9
|
+
it { should have_many(:accounts).through(:memberships) }
|
10
|
+
it { should have_many(:permissions) }
|
11
|
+
it { should have_many(:projects).through(:permissions) }
|
12
|
+
|
13
|
+
it "is an admin of an account with an admin membership" do
|
14
|
+
account = Factory(:account)
|
15
|
+
Factory(:membership, :user => subject, :admin => true, :account => account)
|
16
|
+
subject.should be_admin_of(account)
|
17
|
+
|
18
|
+
subject.memberships.admin.first.account.should == account
|
19
|
+
end
|
20
|
+
|
21
|
+
it "isn't an admin of an account with a non admin membership" do
|
22
|
+
account = Factory(:account)
|
23
|
+
Factory(:membership, :user => subject, :admin => false, :account => account)
|
24
|
+
subject.should_not be_admin_of(account)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "isn't an admin of an account without a membership" do
|
28
|
+
account = Factory(:account)
|
29
|
+
subject.should_not be_admin_of(account)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "is a member with a membership for the given account" do
|
33
|
+
account = Factory(:account)
|
34
|
+
Factory(:membership, :user => subject, :account => account)
|
35
|
+
subject.should be_member_of(account)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "isn't a member without a membership for the given account" do
|
39
|
+
account = Factory(:account)
|
40
|
+
other_account = Factory(:account)
|
41
|
+
Factory(:membership, :user => subject, :account => other_account)
|
42
|
+
subject.should_not be_member_of(account)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "is a member with a membership for the given project" do
|
46
|
+
project = Factory(:project)
|
47
|
+
membership = Factory(:membership, :user => subject,
|
48
|
+
:account => project.account)
|
49
|
+
Factory(:permission, :membership => membership,
|
50
|
+
:project => project)
|
51
|
+
subject.should be_member_of(project)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "isn't a member without a membership for the given project" do
|
55
|
+
project = Factory(:project)
|
56
|
+
other_project = Factory(:project)
|
57
|
+
membership = Factory(:membership, :user => subject,
|
58
|
+
:account => other_project.account)
|
59
|
+
Factory(:permission, :membership => membership,
|
60
|
+
:project => other_project)
|
61
|
+
subject.should_not be_member_of(project)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns users by name" do
|
65
|
+
Factory(:user, :name => "def")
|
66
|
+
Factory(:user, :name => "abc")
|
67
|
+
Factory(:user, :name => "ghi")
|
68
|
+
|
69
|
+
User.by_name.map(&:name).should == %w(abc def ghi)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Saucy routing extensions" do
|
4
|
+
include ActionDispatch::Routing::UrlFor
|
5
|
+
|
6
|
+
let(:_routes) { ActionDispatch::Routing::RouteSet.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
_routes.draw do
|
10
|
+
resources :accounts
|
11
|
+
through :accounts do
|
12
|
+
resources :projects
|
13
|
+
through :projects do
|
14
|
+
resources :discussions
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
extend(_routes.named_routes.module)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows a nested member path to be accessed through just the child's name" do
|
23
|
+
account = stub('account', :to_param => 'abc')
|
24
|
+
project = stub('project', :account => account, :to_param => 'def')
|
25
|
+
project_path(project).should == "/accounts/abc/projects/def"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "allows a nested member url to be accessed through just the child's name" do
|
29
|
+
account = stub('account', :to_param => 'abc')
|
30
|
+
project = stub('project', :account => account, :to_param => 'def')
|
31
|
+
project_url(project, :host => 'example.com').
|
32
|
+
should == "http://example.com/accounts/abc/projects/def"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "allows a nested collection path to be accessed through just the parent's name" do
|
36
|
+
account = stub('account', :to_param => 'abc')
|
37
|
+
projects_path(account).should == "/accounts/abc/projects"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "allows a nested new path to be accessed through just the parent's name" do
|
41
|
+
account = stub('account', :to_param => 'abc')
|
42
|
+
new_project_path(account).should == "/accounts/abc/projects/new"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "allows a doubly nested member path to be access through just the child's name" do
|
46
|
+
account = stub('account', :to_param => 'abc')
|
47
|
+
project = stub('project', :account => account, :to_param => 'def')
|
48
|
+
discussion = stub('discussion', :project => project, :to_param => 'ghi')
|
49
|
+
discussion_path(discussion).should == "/accounts/abc/projects/def/discussions/ghi"
|
50
|
+
end
|
51
|
+
end
|
data/spec/saucy_spec.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Saucy::Configuration do
|
4
|
+
it "has layouts" do
|
5
|
+
subject.layouts.should be_a(Saucy::Layouts)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has a manager_email_address" do
|
9
|
+
subject.manager_email_address.should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has a support_email_address" do
|
13
|
+
subject.support_email_address.should_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a nil merchant_account_id" do
|
17
|
+
subject.merchant_account_id.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can listen for events" do
|
21
|
+
observer = stub("an observer")
|
22
|
+
cleanup_observers do
|
23
|
+
Saucy::Configuration.observe(observer)
|
24
|
+
Saucy::Configuration.observers.should include(observer)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can notify observers" do
|
29
|
+
observer = stub("an observer", :some_event => nil)
|
30
|
+
cleanup_observers do
|
31
|
+
Saucy::Configuration.observe(observer)
|
32
|
+
Saucy::Configuration.notify("some_event", "some_data")
|
33
|
+
observer.should have_received("some_event").with("some_data")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can assign a manager email address" do
|
38
|
+
old_address = subject.manager_email_address
|
39
|
+
begin
|
40
|
+
subject.manager_email_address = 'newsender@example.com'
|
41
|
+
subject.manager_email_address.should == 'newsender@example.com'
|
42
|
+
ensure
|
43
|
+
subject.manager_email_address = old_address
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can assign a support email address" do
|
48
|
+
old_address = subject.support_email_address
|
49
|
+
begin
|
50
|
+
subject.support_email_address = 'newsender@example.com'
|
51
|
+
subject.support_email_address.should == 'newsender@example.com'
|
52
|
+
ensure
|
53
|
+
subject.support_email_address = old_address
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def cleanup_observers
|
58
|
+
yield
|
59
|
+
ensure
|
60
|
+
subject.observers.clear
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
ENV["RAILS_ENV"] ||= 'test'
|
3
|
+
|
4
|
+
if File.exist?("config/environment.rb")
|
5
|
+
require "config/environment"
|
6
|
+
else
|
7
|
+
require File.expand_path("../environment", __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rspec/rails'
|
11
|
+
require 'shoulda'
|
12
|
+
require 'timecop'
|
13
|
+
|
14
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
15
|
+
# in spec/support/ and its subdirectories.
|
16
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
# == Mock Framework
|
20
|
+
#
|
21
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
22
|
+
#
|
23
|
+
# config.mock_with :mocha
|
24
|
+
# config.mock_with :flexmock
|
25
|
+
# config.mock_with :rr
|
26
|
+
config.mock_with :mocha
|
27
|
+
|
28
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
29
|
+
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
30
|
+
|
31
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
32
|
+
# examples within a transaction, remove the following line or assign false
|
33
|
+
# instead of true.
|
34
|
+
config.use_transactional_fixtures = true
|
35
|
+
|
36
|
+
config.backtrace_clean_patterns << %r{gems/}
|
37
|
+
|
38
|
+
config.include AuthenticationHelpers
|
39
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module AuthenticationHelpers
|
2
|
+
def sign_in_as(user)
|
3
|
+
controller.current_user = user
|
4
|
+
end
|
5
|
+
|
6
|
+
def sign_in
|
7
|
+
sign_in_as(Factory(:user))
|
8
|
+
end
|
9
|
+
|
10
|
+
def sign_out
|
11
|
+
controller.current_user = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def current_user
|
15
|
+
controller.current_user
|
16
|
+
end
|
17
|
+
|
18
|
+
def signed_in?
|
19
|
+
controller.signed_in?
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign_in_as_admin_of_account(account)
|
23
|
+
user = Factory(:user)
|
24
|
+
Factory(:membership, :user => user, :account => account, :admin => true)
|
25
|
+
sign_in_as user
|
26
|
+
end
|
27
|
+
|
28
|
+
def sign_in_as_non_admin_of_account(account)
|
29
|
+
user = Factory(:user)
|
30
|
+
Factory(:membership, :user => user, :account => account, :admin => false)
|
31
|
+
sign_in_as user
|
32
|
+
end
|
33
|
+
|
34
|
+
def sign_in_as_non_admin_of_project(project)
|
35
|
+
user = Factory(:user)
|
36
|
+
membership = Factory(:membership, :user => user,
|
37
|
+
:account => account,
|
38
|
+
:admin => false)
|
39
|
+
Factory(:permission, :membership => membership,
|
40
|
+
:project => project)
|
41
|
+
sign_in_as user
|
42
|
+
end
|
43
|
+
|
44
|
+
def sign_in_as_admin_of_project(project)
|
45
|
+
user = Factory(:user)
|
46
|
+
membership = Factory(:membership, :user => user,
|
47
|
+
:account => account,
|
48
|
+
:admin => true)
|
49
|
+
Factory(:permission, :membership => membership,
|
50
|
+
:project => project)
|
51
|
+
sign_in_as user
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec::Matchers.define :be_signed_in do
|
56
|
+
match do |controller|
|
57
|
+
@controller = controller
|
58
|
+
@controller.signed_in? &&
|
59
|
+
(@expected_user.nil? || @expected_user == @controller.current_user)
|
60
|
+
end
|
61
|
+
|
62
|
+
chain :as do |user|
|
63
|
+
@expected_user = user
|
64
|
+
end
|
65
|
+
|
66
|
+
failure_message_for_should do
|
67
|
+
message = "expected to be signed in"
|
68
|
+
message << " as #{@expected_user.inspect}" if @expected_user
|
69
|
+
message << " but was "
|
70
|
+
if @controller.signed_in?
|
71
|
+
message << "signed in as #{@controller.current_user.inspect}"
|
72
|
+
else
|
73
|
+
message << "not signed in"
|
74
|
+
end
|
75
|
+
message
|
76
|
+
end
|
77
|
+
|
78
|
+
failure_message_for_should_not do
|
79
|
+
"didn't expect to be signed in"
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module AuthorizationHelpers
|
2
|
+
module AccountAdminExampleGroup
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
let(:account) { Factory(:account) }
|
6
|
+
before { sign_in_as_admin_of_account(account) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module AccountMemberExampleGroup
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
included do
|
13
|
+
let(:account) { Factory(:account) }
|
14
|
+
before { sign_in_as_non_admin_of_account(account) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ProjectAdminExampleGroup
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
included do
|
21
|
+
let(:account) { Factory(:account) }
|
22
|
+
let(:project) { Factory(:project, :account => account) }
|
23
|
+
before { sign_in_as_admin_of_project(project) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ProjectMemberExampleGroup
|
28
|
+
extend ActiveSupport::Concern
|
29
|
+
included do
|
30
|
+
let(:account) { Factory(:account) }
|
31
|
+
let(:project) { Factory(:project, :account => account) }
|
32
|
+
before { sign_in_as_non_admin_of_project(project) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module UserExampleGroup
|
37
|
+
extend ActiveSupport::Concern
|
38
|
+
included do
|
39
|
+
let(:user) { Factory(:user) }
|
40
|
+
before { sign_in_as(user) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
RSpec.configure do |config|
|
46
|
+
config.include AuthorizationHelpers::AccountAdminExampleGroup,
|
47
|
+
:as => :account_admin
|
48
|
+
config.include AuthorizationHelpers::AccountMemberExampleGroup,
|
49
|
+
:as => :account_member
|
50
|
+
config.include AuthorizationHelpers::ProjectAdminExampleGroup,
|
51
|
+
:as => :project_admin
|
52
|
+
config.include AuthorizationHelpers::ProjectMemberExampleGroup,
|
53
|
+
:as => :project_member
|
54
|
+
config.include AuthorizationHelpers::UserExampleGroup,
|
55
|
+
:as => :user
|
56
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ClearanceMatchers
|
2
|
+
class DenyAccessMatcher
|
3
|
+
include Shoulda::ActionController::Matchers
|
4
|
+
|
5
|
+
def initialize(context)
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(controller)
|
10
|
+
if @method
|
11
|
+
@context.__send__(@method, *@args)
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
if @flash
|
16
|
+
controller.should set_the_flash.to(@flash)
|
17
|
+
else
|
18
|
+
controller.should_not set_the_flash
|
19
|
+
end
|
20
|
+
|
21
|
+
url = controller.__send__(:sign_in_url)
|
22
|
+
controller.should redirect_to(url).in_context(@context)
|
23
|
+
|
24
|
+
true
|
25
|
+
rescue RSpec::Expectations::ExpectationNotMetError => failure
|
26
|
+
@failure_message = failure.message
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def flash(flash)
|
32
|
+
@flash = flash
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def on(method, *args)
|
37
|
+
@method = method
|
38
|
+
@args = args
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure_message_for_should
|
43
|
+
@failure_message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def deny_access
|
48
|
+
DenyAccessMatcher.new(self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.include ClearanceMatchers
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class RecordedEvent
|
2
|
+
attr_reader :name, :data
|
3
|
+
|
4
|
+
def initialize(name, data)
|
5
|
+
@name = name.to_s
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"<Event:#{name} #{inspect_data}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
name == other.name && data == other.data
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def inspect_data
|
20
|
+
data.inject([]) { |result, (key, value)|
|
21
|
+
result << "#{key.inspect} => #{value.inspect.slice(0, 20)}"
|
22
|
+
}.join(", ")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class RecordingObserver
|
27
|
+
attr_reader :events
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@events = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(name, data)
|
34
|
+
@events << RecordedEvent.new(name, data)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec::Matchers.define :notify_observers do |event_name, data|
|
39
|
+
match do |ignored_subject|
|
40
|
+
@event = RecordedEvent.new(event_name, data)
|
41
|
+
recorder.events.should include(@event)
|
42
|
+
end
|
43
|
+
|
44
|
+
failure_message do
|
45
|
+
"Expected event:\n#{@event.inspect}\n\nGot events:\n#{recorder.events.map(&:inspect).join("\n")}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def recorder
|
49
|
+
Saucy::Configuration.observers.last
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.before do
|
55
|
+
Saucy::Configuration.observers = [RecordingObserver.new]
|
56
|
+
end
|
57
|
+
end
|